Flutter 詳解 (六、深入瞭解Stream)
阿新 • • 發佈:2020-08-25
檔案上傳
目錄
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 end2. 注意事項
- 為保證伺服器安全,上傳檔案應該放在外界無法直接訪問的目錄下,比如放在WEB-INF目錄下
- 為防止檔案覆蓋的現象發生,要為上傳的檔案產生一個唯一的檔名(時間戳,uuid,md5,位運算演算法)
- 要限制上傳檔案的最大值(節省硬碟空間)
- 可以限制上傳檔案的型別,在收到上傳檔名時,判斷後綴名是否合法(防止傳太大的檔案)
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); } }