添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • Data url
  • URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents.
  • Ref: Data URLs
  • base64 格式
  • Base64 is a group of binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. The term Base64 originates from a specific MIME content transfer encoding.
  • Ref: Base64 - Wikipedia

    相關技術文件

  • Unit 06 - 上傳及下載檔案
  • 17.16 The fileupload Example Application with Servlet 3.0 - Java Platform, Enterprise Edition: The Java EE Tutorial (Release 7)
  • 相關 API

    Java 9

  • java.util.Base64
  • java.io.InputStream
  • Demo Source Code

    Demo Code: t11 directory @ jsf_under_training_codes - Bitbucket

    Project Dependency

  • 使用 Primeface 8.0 及 lombok 1.8.12
  • 部分 pom.xml 内容
  • <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.primefaces/primefaces -->
    <dependency>
        <groupId>org.primefaces</groupId>
        <artifactId>primefaces</artifactId>
        <version>8.0</version>
    </dependency>
      
  • 因爲使用了 lombok project 自動產生 getter 及 setter, Maven compiler plugin 要額外設定.
  • Maven compiler plugin 的設定:
  •  <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.1</version>
         <configuration>
             <source>1.8</source>
             <target>1.8</target>
             <compilerArguments>
                 <endorseddirs>${endorsed.dir}</endorseddirs>
             </compilerArguments>
             <!--Set the lombok for the compiler plugin--> 
             <annotationProcessorPaths>
                     <groupId>org.projectlombok</groupId>
                     <artifactId>lombok</artifactId>
                     <version>1.18.12</version>
                 </path>
             </annotationProcessorPaths>
         </configuration>
     </plugin>
    

    PrimeFaces 設定

  • 使用了 PrimeFaces 的 <p:fileUpload> 上傳檔案.
  • 此 tag 可使用 Servlet 3.0 或者 Apache Commons, 需要額外設定.
  • 參考 PrimeFaces Documentation
  • 加入 web.xml 的設定内容:
  •  <context-param>
            <param-name>primefaces.UPLOADER</param-name>
            <param-value>auto</param-value>
        </context-param>
    

    Web Components (CDI Beans) 建立

    檔案上傳處理程序

  • JSF 透過 value binding, 將 file bind 到 某個 Bean property.
  • 該 Bean property 的資料型態為 javax.servlet.http.Part
  • Part 物件取得 inputStream, 讀取内容到 byte[]
  • 使用 Base64.Base64.getEncoder().encodeToString()byte[] 的内容轉成 base64 string format.
  • 若要在 <img>src 屬性中直接使用 base64 格式圖片内容, 需要加上 media type 及 編碼格式指示: data:image/png;base64,
  • h:form 的編碼方式要改成 enctype="multipart/form-data"
  • 使用 Part 物件取得 InputStream 物件, 以讀取圖片內容
  • 將圖片內容存到 byte []
  • 使用 Base64 物件對 byte[] 內圖片內容進行 Base64 編碼
  • 取得圖片的 Base64 内容編碼之後, 將其值設定到 bean property base64Photo, 以利後續使用 <img> 顯示圖片
  • private Part uploadPart; * Save file to the file system in the server-side. * @return null (Stay in the same page). public String uploadAction() { if (uploadPart == null) { return null; //Get the input string from the Part Object InputStream is = null; try { is = uploadPart.getInputStream(); // Convert the input stream to the byte array // Ref: https://magiclen.org/java-base64/ byte[] bytes = is.readAllBytes(); // Convert the base64 string to data uri // Ref: https://blog.gtwang.org/web-development/minimizing-http-request-using-data-uri/ // Schema: data:[<media type>][;base64],<data> String base64Str = Base64.getEncoder().encodeToString(bytes); this.base64Photo = "data:image/png;base64," + base64Str; } catch (IOException ex) { Logger.getLogger(UploadBean.class.getName()).log(Level.SEVERE, null, ex); //Set the file name property filename = uploadPart.getSubmittedFileName(); return null; // Stay in the same page

    JSF Page

    Form to upload the file

  • 表單的 enctype 要設定成 multipart/form-data
  • enctype 資料上傳到伺服器時瀏覽器使用的編碼型別
  • multipart/form-data 會將表單内的每個欄位各自編碼
  • 參考: 17.16 The fileupload Example Application - Java Platform, Enterprise Edition: The Java EE Tutorial (Release 7)
  •  <h2>File upload with html tag</h2>
     <h:form enctype="multipart/form-data">
         <h:panelGrid columns="2">
             <h:outputLabel for="selectFile">Choose a file</h:outputLabel>
             <h:inputFile id="selectFile" value="#{uploadBean.uploadPart}"/>
             <h:commandButton value="Upload" action="#{uploadBean.uploadAction}"/>
         </h:panelGrid>
     </h:form>
    

    Element to display the image using base64 format

    <h2>Uploaded Photo</h2>
    <p>Filename: #{uploadBean.filename}</p>
    <img jsf:id="display" src="#{uploadBean.base64Photo}" />
    

    實作 2: 使用 Ajax 的方式, 選擇圖片後, 自動上傳圖片

  • 修改實作 1 的程式碼
  • 監聽上傳圖片的 <h:inputFile>valueChange ajax event
  • valueChange ajax event 被觸發時, 提出請求(request), 只將 <h:inputFile> 元素送往後端 Server 處理
  • Server 端請求處理完畢後, 回應 Browser 處理結果, 用以更新 HTML 的 DOM 模型。我們指定更新顯示上傳圖片的元素。
  • JSF 頁面

    <h2>File upload with html tag and Ajax</h2>
    <h:form enctype="multipart/form-data">
        <h:panelGrid columns="2">
            <h:outputLabel>Field 1 </h:outputLabel>
            <h:inputText value="#"></h:inputText>
            <h:outputLabel for="selectFile">Choose a file</h:outputLabel>
            <h:inputFile id="selectFile" value="#{uploadBean.uploadPart}">
            <!-- #1 -->
                <f:ajax event="valueChange" execute="@this" render=":display"
                        listener="#{uploadBean.handleFileUploadAjaxListener}"/>
            </h:inputFile>
        </h:panelGrid>
    </h:form>
    <h2>Uploaded Photo</h2>
    <p>Filename: #{uploadBean.filename}</p>
    <!--passthrough element; HTML tag friendly-->
    <img jsf:id="display" src="#{uploadBean.base64Photo}" />
    <br />
    標示 #1:

  • 使用 <f:ajax> 使元素具備 Ajax 的能力
  • 屬性 event="valueChange": 監聽上傳圖片的 <h:inputFile>valueChange ajax event
  • 屬性 execute="@this": 事件觸發後, 將 <f:ajax> 的父元素送交後端 Server 處理
  • 屬性 listener="#{uploadBean.handleFileUploadAjaxListener}": 事件觸發後, 要執行的 bean method
  • 屬性 render=":display": 請求處理後, 重新 render id 為 display 的元素
  • Ajax 事件的 event listener

    @Named(value = "uploadBean")
    @SessionScoped
    // Use lombok to generate the getters and setters. See: https://kucw.github.io/blog/2020/3/java-lombok/ 
    @Getter
    @Setter
    public class UploadBean implements Serializable {
        private Part uploadPart;
        private String filename = "";
        private String base64Photo;
    // #1
    public void handleFileUploadAjaxListener(AjaxBehaviorEvent event) {
            this.base64Photo = toBase64Str(uploadPart);
            this.filename = uploadPart.getSubmittedFileName();
    // #2    
    private String toBase64Str(Part part){
            String result ="";
            try {
                InputStream is = uploadPart.getInputStream();
                // Convert the input stream to the byte array
                // Ref: https://magiclen.org/java-base64/
                byte[] bytes = is.readAllBytes();
                // Convert the base64 string to data uri
                // Ref: https://blog.gtwang.org/web-development/minimizing-http-request-using-data-uri/
                // Schema:  data:[<media type>][;base64],<data>
                String base64Str = Base64.getEncoder().encodeToString(bytes);
                result = "data:image/png;base64," + base64Str;
            } catch (IOException ex) {
                Logger.getLogger(UploadBean.class.getName()).log(Level.SEVERE, null, ex);
            return result;
    

    標示 #1 說明:

  • Ajax 事件觸發後, 圖片檔案會先儲存到 bean property uploadPart 中, 之後再執行 Ajax event listener
  • Ajax event listener 方法的簽名:
  • public void ajaxListenerName(AjaxBehaviorEvent event)