Monday, November 28, 2011

自訂標籤

可以把JSP會共用到的邏輯判斷、特殊畫面呈現,拉出來做一個 tag
比方說,在資料新、刪、修,成功後,要加一個顯示訊息
<!-- 畫面新增成功的訊息 -->
<c:if test="${not empty createMessage }">
    <div id="createFoo" class="highlight-css" style="padding:0 0.7em;">
         <p>
           <span class="icon-css"
                 style="float:left; margin-right:5px;"></span>
                <fmt:message key="${ createMessage }"/>
        </p>
    </div>
</c:if>
<script type="text/javascript">
    //畫面延遲2秒後,fadeOut
    $(function() {
    $("#${createFoo}").delay(2000).fadeOut(2000);
    });
</script> 

<!-- 畫面修改成功的訊息 -->
<c:if test="${not empty modifyMessage }">
    <div id="modifyFoo" class="highlight-css" style="padding:0 0.7em;">
         <p>
           <span class="icon-css"
                 style="float:left; margin-right:5px;"></span>
           <fmt:message key="${ modifyMessage }"/>
        </p>
    </div>
</c:if>
<script type="text/javascript">
  $(function() {
    $("#${modifyFoo}").delay(2000).fadeOut(2000);
    });
</script>

<!-- 畫面刪除成功的訊息 -->
<c:if test="${not empty modifyMessage }">
    <div id="removeFoo" class="highlight-css" style="padding:0 0.7em;">
         <p>
           <span class="icon-css" style="float:left; margin-right:5px;"></span>
           <fmt:message key="${ modifyMessage }"/>
        </p>
    </div>
</c:if>
<script type="text/javascript">
    $(function() {
    $("#${removeFoo}").delay(2000).fadeOut(2000);
    });
</script>
有沒有覺的快瘋了,一直在 do loop   =口 =
那就把這一段拉出來,做tag。

第一步:在 WEB-INF\tags 下面,建立一個,延伸檔名為:tag的檔案
若用eclipse的話,可以選擇新建一個 JSP Tag
 image

第二步用法:
可以將各種格式 ( *日期 )、或CSS樣板、或JS特效,通通放進來
在JSP就只需要單純的引用自訂的標籤
以上面的範列來說,就可以改成這樣:
   1: <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
   2: <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
   3: <%@ attribute name="messageId" required="true" %>  
   4: <%@ attribute name="key" required="false" %>
   5: <!-- 自訂傳進來的參數,required="true"為必要值 -->
   6: <fmt:setBundle basename="message.bundle.path"/>
   7: <c:if test="${not empty key}">
   8:     <div id="${ messageId }" 
   9:             class="highlight-css" 
  10:             style="padding:0 0.7em;">
  11:          <p>
  12:             <span class="icon-css" 
  13:                       style="float:left; margin-right:5px;">
  14:            </span>
  15:            <fmt:message key="${key}"/>
  16:         </p>
  17:     </div>
  18:     <script type="text/javascript">
  19:     <!--//共用的script 效果
  20:     $(function() {
  21:         $("#${messageId}").delay(2000).fadeOut(2000);
  22:     });
  23:     -->
  24:     </script>    
  25: </c:if>

JSP使用:
  <%@ taglib prefix="frog" tagdir="/WEB-INF/tags" %>

<ris:statusMessage messageId="processMessage" key="${message}" />

這樣,有沒有覺得跳出地獄迴圈了~

Saturday, November 26, 2011

VIM中每行一相反順序排列

在VIM看log檔時,有時會希望從最舊的開始看,有時則想從最新的;或是,不知什麼鬼狀況看到的文字檔每行就是剛好是我想看順序的相反。通常都是後者的強況比較多,鬼狀況。

當然VIM最喜歡的就是鬼狀況。以下的指令可以直接將每行順序反轉。
:g/^/m0

以上是正規表示式(Regular expression)。
  • 開頭的g代表要對每個有比對的行執行動作。
  • ^代表要比對到的內容是每一行的開頭,因為每一行一定有開頭所以都會比對到。
  • m是移動。0則代表移到第0行,即檔案的最開始。

所以指令執行的時候會是
  1. 選第一行,將第一行移到檔案最頂端。
  2. 選第二行,將第二行移到檔案最頂端。
  3. 選第三行,將第三行移到檔案最頂端。
  4. ...
依此類推。

Thursday, November 24, 2011

Encoding or code set not supported


-79783 Encoding or code set not supported
The encoding or code set entered in the DB_LOCALE or CLIENT_LOCALE
variable is not valid. Check the "Informix JDBC Driver Programmer's Guide"
for valid code sets.

當存單一英文字母時,是沒有問題的;
但是存一個中文字時,就會出現這個SQL Exception

而,這個table的欄位,我是這樣開的:colName   NVARCHAR(1),
因為需求是,只會存單一個字元

資料庫是 informix ,jsp的編碼是utf-8,資料庫連線設定是:
   1: jdbc:informix-sqli://localhost:9088/ibm:informixserver=ol_informix1170;NEWCODESET=utf8,8859-1,819

解法很奇怪:

把 資料庫欄位的長度加到四,colName   NVARCHAR(4),
沒有改任何設定,資料就可以正常搜尋出來了
或許是 utf-8 編碼用到四個 byte !?

Wednesday, November 9, 2011

javaScript, checkBox 全選

image
常常會有這樣的需求,選最上面的checkbox,要連動讓下面的checkbox也勾選;
如果,最上面的checkbox取消勾選,也要下面的checkbox取消勾選。
不想寫死 checked=true 或 false,可以用這個方法
   1: <script type="text/javascript">
   2:  
   3: function checked(){
   4:     var checkValue = document.getElementById('checkAll').checked;
   5:     
   6:     document.getElementById('check1').checked=checkValue;
   7:     document.getElementById('check2').checked=checkValue;
   8:     document.getElementById('check3').checked=checkValue;
   9:     document.getElementById('check4').checked=checkValue;
  10: }
  11:  
  12: </script>


   1: <input type="checkbox" id="checkAll" onclick="checked()"/>
   2: <input type="checkbox" id="check1"/>
   3: <input type="checkbox" id="check2"/>
   4: <input type="checkbox" id="check3"/>
   5: <input type="checkbox" id="check4"/>    

使用HttpServlet做檔案下載


檔案下載是網站程式中常見的需求。也許只是一般檔案要下載,或許是動態產生的圖檔。有時候把檔案直接放在公開的網站目錄中直接給他的連結給人下載就可以了。不過當成是稍為複雜時,這些檔案需要與網站部屬目錄分開處理,或者動態產生的內容不經過寫到檔案直接用stream的方式傳出來。

以下是一個共用的Servlet,提供最基本的檔案下載功能。他設為Abstract不能直接使用,必須另外寫一個繼承它的Servlet類別。他最主要提供了sendDownloadFile的兩個多載的方法。一個提供了直接是用stream傳輸資料的放方,另一個只須給電腦中檔案的完整路徑就可以傳輸該檔案了。

所以,很簡單,繼承FileDownloadServlet,在doGet或doPost中寫自己程式的邏輯,如找出要傳輸的檔案或檢查使用者權限等。然後把檔案完整路徑或產生的stream傳給sendDownloadFile好了。

在沒有特別的Framework支援,或在Portal下。這個Servlet可以簡化很多工作。

這個是FileDownloadServlet::
   1: import java.io.BufferedInputStream;
   2: import java.io.File;
   3: import java.io.FileInputStream;
   4: import java.io.FileNotFoundException;
   5: import java.io.IOException;
   6: import java.io.InputStream;
   7: import java.io.OutputStream;
   8:  
   9: import javax.servlet.http.HttpServlet;
  10: import javax.servlet.http.HttpServletResponse;
  11: /**
  12:  * 檔案下載共用元件
  13:  */
  14: public abstract class FileDownloadServlet extends HttpServlet {
  15:     private static final long serialVersionUID = 716097697136220545L;
  16:  
  17:     private static final int DEFAULT_STREAM_BUFFER_SIZE  = 1024;
  18:     private static final String CONTENT_DISPOSITION_FORMAT = "%s; filename=\"%s\"";
  19:         
  20:     private String contentDisposition = "attachment";
  21:  
  22:     protected int getStreamBufferSize() {
  23:         return DEFAULT_STREAM_BUFFER_SIZE;
  24:     }
  25:  
  26:     //叫瀏覽器直接開在瀏覽器裡
  27:     protected void setContentDispositionInline() {
  28:         contentDisposition = "inline";
  29:     }
  30:  
  31:     //存檔就好
  32:     protected void setContentDispositionAttachment() {
  33:         contentDisposition = "attachment";
  34:     }
  35:     
  36:     protected void sendDownloadFile(HttpServletResponse response, InputStream in, int contentLength, String contentType, String saveAsFileName) throws IOException {
  37:         response.setContentType(contentType);
  38:         response.setContentLength(contentLength);
  39:         response.setHeader("Content-Disposition", String.format(CONTENT_DISPOSITION_FORMAT, contentDisposition, saveAsFileName));
  40:  
  41:         byte[] buf = new byte[getStreamBufferSize()];
  42:         BufferedInputStream from = new BufferedInputStream(in);
  43:         OutputStream to = response.getOutputStream();
  44:         
  45:         int length = from.read(buf);
  46:         while(length > 0) {
  47:             to.write(buf, 0, length);
  48:             length = from.read(buf);
  49:         }
  50:         
  51:         to.flush();
  52:         to.close();
  53:     }
  54:     
  55:     protected void sendDownloadFile(HttpServletResponse response, String fullFilePath, String saveAsFileName) throws FileNotFoundException, IOException {
  56:         File file = new File(fullFilePath);
  57:         String contentType = getServletConfig().getServletContext().getMimeType(fullFilePath);
  58:         InputStream in = new FileInputStream(file);
  59:         sendDownloadFile(response, in, (int)file.length(), contentType, saveAsFileName);
  60:         in.close();
  61:     }
  62:  
  63: }


讓伺服器能夠與瀏覽器傳輸檔案的方式是指定contentType。一般的網頁contentType是text/html,瀏覽器認得此資料類別直接將它顯示出來,就是我們每天看到的網頁了。檔案傳輸只是將contentType改了一下,剩下的就是將要傳送的資料以串流方式傳出。

在使用完整路徑傳送檔案的sendDownloadFile方法中,它呼叫了getServletConfig().getServletContext().getMimeType(fileFullPath),用意是請執行程式的容器(Tomcat或其他Java EE server)依據傳入的檔名給我們正確的contentType。

sendDownloadFile的saveAsFileName是告訴瀏覽器存檔時預設的檔名,設在contentDisposition。

可以覆寫getStreamBufferSize回傳在傳輸檔案時使用buffer的大小,沒有覆寫的話會使用DEFAULT_STREAM_BUFFER_SIZE,也就是1024。

呼叫setContentDispositionInline跟setContentDispositionAttachment分別代表告訴瀏覽器要直接顯示或存檔。不過要不要照著做還是要看瀏覽器乖不乖聽不聽話。


接下來是一個繼承FileDownloadServlet的servlet,這隻servlet主要在接網址列傳過來叫做id的參數
 使用的路徑可能是 http://localhost/AttachmentDownload?id=120
他會去資料庫查詢該筆資料的其它欄位資料:如檔名和檔案實際存放的路徑,

   1: import java.io.File;
   2: import java.io.IOException;
   3:  
   4: import javax.servlet.ServletConfig;
   5: import javax.servlet.ServletException;
   6: import javax.servlet.http.HttpServlet;
   7: import javax.servlet.http.HttpServletRequest;
   8: import javax.servlet.http.HttpServletResponse;
   9:  
  10: import org.springframework.beans.factory.annotation.Autowired;
  11: import org.springframework.web.context.support.SpringBeanAutowiringSupport;
  12:  
  13: public class AttachmentDownloadServlet extends FileDownloadServlet {
  14:     private static final long serialVersionUID = 1L;
  15:     /**
  16:      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
  17:      */
  18:     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  19:         int id = Integer.parseInt(request.getParameter("id"));
  20:
  21:         
  22:     //取出檔案實際存放的路徑,getFilePath請依照個別需實作
  23:         File filePath = new File(getFilePath(id), attachment.getFileName());
  24:     
  25:     //解決微軟存檔名時,會連 "c:/" 一起存    
  26:         File fileName = new File(attachment.getOriginalName());
  27:         sendDownloadFile(response, fullFilePath, fileName.getName());
  28:     }
  29:  
  30:     /**
  31:      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
  32:      */
  33:     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  34:         doGet(request, response);
  35:     }


web.xml的設定

   1: <!-- 檔案下載  -->
   2: <servlet>
   3:     <display-name>AttachmentDownloadServlet</display-name>
   4:     <servlet-name>AttachmentDownloadServlet</servlet-name>
   5:     <servlet-class>com.example.servlet.AttachmentDownloadServlet</servlet-class>
   6: <sservlet>
   7:   
   8: <servlet-mapping>
   9:     <servlet-name>AttachmentDownloadServlet</servlet-name>
  10:     <url-pattern>/AttachmentDownload</url-pattern>
  11: <sservlet-mapping>


重點是如何在畫面上,呼叫這隻servlet
這個列子是可以下載多個檔案,的寫法

   1: <c:forEach var="uploaded" items="${ bean.uploadFiles }">
   2:  
   3: <!-- 在jsp中,組出呼叫 servlet 的 url,並在後面加一個id的參數 -->
   4: <c:url value="/AttachmentDownload" var="downloadUrl">
   5:     <c:param name="id" value="${uploaded.id}"></c:param>
   6: </c:url>
   7:  
   8: <!-- 組出來的網址:http://localhost/AttachmentDownload?id=120 -->
   9: <a href="${downloadUrl}"><c:out value="${uploaded.originalName}"/></a>
  10: </forEach>