1. 程式人生 > 實用技巧 >Flutter 詳解 (六、深入瞭解Stream)

Flutter 詳解 (六、深入瞭解Stream)

檔案上傳

目錄

1. 檔案上傳基礎

graph LR id1[使用者] id2[客戶端<br>C] id3[Web應用] id4[File System] id5[讓瀏覽器能夠支援檔案上傳] id1 --下載圖片-->id2 id2 --Network<br>網路傳輸--> id3 id2 --> id5 subgraph 伺服器<br>S id3 id4 id3 --存--> id4 end

2. 注意事項

  1. 為保證伺服器安全,上傳檔案應該放在外界無法直接訪問的目錄下,比如放在WEB-INF目錄下
  2. 為防止檔案覆蓋的現象發生,要為上傳的檔案產生一個唯一的檔名(時間戳,uuid,md5,位運算演算法)
  3. 要限制上傳檔案的最大值(節省硬碟空間)
  4. 可以限制上傳檔案的型別,在收到上傳檔名時,判斷後綴名是否合法(防止傳太大的檔案)

3. index.jsp

<%--
  Created by IntelliJ IDEA.
  User: Wang
  Date: 2020/8/25
  Time: 16:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <%--通過表單上傳檔案
    get: 上傳檔案大小有限制
    post: 上傳檔案大小無限制
    檔案上傳必須設定enctype="multipart/form-data
  --%>
  <%--${pageContext.request.contextPath}  獲取伺服器路徑--%>
  <form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
    上傳使用者: <input type="text" name="username"><br/>
    <p><input type="file" name="file1"></p>
    <p><input type="file" name="file2"></p>
    <p><input type="submit" value="提交">  |   <input type="reset" value="重置"></p>

  </form>



  </body>
</html>

4. 檔案上傳的Java程式碼

package com.wang.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

public class FileServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        //判斷上傳的檔案是普通表單還是帶檔案的表單
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;     //終止方法執行,說明這是一個普通的表單,直接返回
        }

        //建立上傳檔案的儲存路徑,建議儲存在WEB-INF下.安全,使用者無法直接訪問上傳的檔案
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()) {
            uploadFile.mkdir();     //如果第一次上傳,檔案的路徑不存在,則建立這個路徑
        }

        //快取,臨時檔案
        //臨時路徑,假如檔案超過了預期的大小,我們就把它放在一個臨時檔案中,過幾天自動刪除,或者提醒使用者轉存為永久
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if (!tmpFile.exists()) {
            tmpFile.mkdir();     //如果第一次上傳,檔案的路徑不存在,則建立這個路徑
        }

        //處理上傳的檔案,一般都需要通過流來獲取,我們可以使用request.getInputStream(),原生態的檔案上傳流獲取,十分麻煩
        //但是我們都建議使用Apache的檔案上傳元件來實現,common-fileload,它需要依賴於commons-io元件

        /*
        ServletFileUpload負責上傳處理的檔案,並將表單中每個輸出項封裝成一個FileItem物件
        在使用ServletFileUpload物件解析請求時需要DiskFileItemFactory物件.
        所以,我們需要在進行解析工作前構造好DiskFileItemFactory物件.
        通過ServletFileUpload物件的構造方法或setFileItemFactory()方法設定ServletFileUpload物件的fileItemFactory屬性
         */

        //1.建立DiskFileItemFactory物件,處理檔案上傳路徑或者大小限制
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //通過這個工廠設定一個快取區,當上傳的檔案大於這個快取區的時候,將他放到臨時檔案中
        factory.setSizeThreshold(1024 * 1024);  //快取區大小為1M
        factory.setRepository(tmpFile);         //臨時目錄的儲存路徑,需要一個File

        //2.獲取ServletFileUpload
        ServletFileUpload upload = new ServletFileUpload(factory);
        //監聽檔案上傳進度:
        upload.setProgressListener(new ProgressListener() {
            @Override
            //pBytesRead:已經讀取到的檔案大小
            //pContentLength:檔案大小
            public void update(long pByteRead, long pContentLength, int PItem) {
                System.out.println("總大小: " + pContentLength + "已上傳: " + pByteRead);
            }
        });

        //處理亂碼問題
        upload.setHeaderEncoding("UTF-8");
        //設定單個檔案的最大值
        upload.setFileSizeMax(1024 * 1024 * 10);
        //設定總共能夠上傳的檔案的大小
        //1024kb * 1024kb = 1M
        upload.setSizeMax(1024 * 1024 * 10);

        //3.處理上傳檔案
        //把前端請求解析,封裝成一個FileItem物件,需要從ServletFileUpload物件中獲取
        try {
            List<FileItem> fileItems = upload.parseRequest(request);
            //fileItem  每一個表單物件
            for (FileItem fileItem : fileItems) {
                //判斷上傳的檔案是普通的表單還是帶檔案的表單
                if (fileItem.isFormField()) {
                    //getFieldName指的是前端表單控制元件的name
                    String name = fileItem.getFieldName();
                    //處理亂碼
                    String value = fileItem.getString("UTF-8");
                    System.out.println(name + " : " + value);
                } else {
                    //檔案的情況下
                    //==================處理檔案====================
                    String uploadFileName = fileItem.getName();
                    //可能存在檔名不合法的情況
                    if (uploadFileName.trim().equals("")) {
                        continue;
                    }
                    //獲得上傳的檔名  /images/pic.png
                    String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                    //獲得檔案的字尾名
                    String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
                    /*
                        如果檔案字尾名不是我們所需要的
                        就直接return,不處理,告訴使用者檔案型別不對
                     */

                    //可以使用UUID(唯一識別的通用碼),保證檔名唯一
                    //UUID.randomUUID(),隨機生成一個唯一識別的通用碼
                    //網路傳輸中的東西,都需要序列化
                    //POJO,實體類,如果想要在多個電腦上執行,需要傳輸,需要把物件都序列化了
                    //JNI = Java Native Interface
                    //implements Serializable 序列化的標記介面  JVM ==> 本地方法棧   native ==> C++

                    String uuidPath = UUID.randomUUID().toString();

                    //==================存放地址====================

                    //存到哪裡? uploadPath
                    //檔案真實存在的路徑 realPath
                    String realPath = uploadPath + "/" + uuidPath;
                    //給每一個檔案建立一個對應的資料夾
                    File realPathFile = new File(realPath);
                    if (!realPathFile.exists()) {
                        realPathFile.mkdir();
                    }

                    //==================檔案傳輸=====================
                    //獲得檔案上傳的流
                    InputStream inputStream = fileItem.getInputStream();

                    //建立一個檔案輸出流
                    //realPath = 真實的資料夾
                    //檔案:加上輸出的檔案的名字 + "/" + uuidFileName
                    FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);

                    //建立一個緩衝區
                    byte[] buffer = new byte[1024 * 1024];

                    //判斷是否讀取完畢
                    int len = 0;
                    //如果大於0說明還存在資料
                    while ((len = inputStream.read(buffer)) > 0) {
                        fos.write(buffer,0, len);
                    }

                    //關閉流
                    fos.close();
                    inputStream.close();

                    //上傳成功,清除臨時檔案
                    fileItem.delete();
                }
            }

        } catch (FileUploadException e) {
            e.printStackTrace();
        }

        //Servlet請求轉發訊息
        request.getRequestDispatcher("info.jsp").forward(request, response);

    }

}