day18_文件的上傳和下載學習筆記

来源:https://www.cnblogs.com/chenmingjun/archive/2018/06/03/9131355.html
-Advertisement-
Play Games

一、文件的上傳和下載 1、文件上傳的原理分析 什麼是文件上傳? 要將客戶端(瀏覽器)數據存儲到伺服器端,而不將數據直接存儲到資料庫中,而是要將數據存儲到伺服器所在的磁碟上,這就要使用文件上傳。為什麼使用文件上傳? 通過文件上傳,可以將瀏覽器端的數據直接保存到伺服器端。不將數據保存到資料庫中,而是保存 ...


一、文件的上傳和下載

1、文件上傳的原理分析

什麼是文件上傳?
    要將客戶端(瀏覽器)數據存儲到伺服器端,而不將數據直接存儲到資料庫中,而是要將數據存儲到伺服器所在的磁碟上,這就要使用文件上傳。
為什麼使用文件上傳?
    通過文件上傳,可以將瀏覽器端的數據直接保存到伺服器端。不將數據保存到資料庫中,而是保存到伺服器磁碟上,這樣減少了資料庫伺服器的壓力,對數據的操作更加靈活。

1.1 文件上傳的必要前提

  • a、提供form表單,method必須是post提交方式。
  • b、form表單必須設置為enctype="multipart/form-data"
  • c、提供input type="file"類的上傳輸入域。

1.2 enctype屬性

    作用:告知伺服器請求正文的MIME類型(文件類型)。(與請求消息頭中:Content-Type作用是一致的)
    可選值

  • application/x-www-form-urlencoded(預設)
        請求消息正文:name=tom&photo=a.txt
        伺服器獲取數據:String name = request.getParameter("name");



  • multipart/form-data
        請求消息正文:
        伺服器獲取數據:request.getParameter(String)方法獲取指定的表單欄位字元內容,但文件上傳表單已經不再是字元內容,而是位元組內容,所以失效,所以需要位元組流的方式。



    文件上傳解析請求正文的每部分的內容。

2、藉助第三方的上傳組件實現文件上傳

2.1 fileupload概述

    fileupload是由apachecommons組件提供的上傳組件。它最主要的工作就是幫我們解析request.getInputStream()
    使用步驟,導入commons-fileupload相關jar包
        commons-fileupload.jar,核心包。
        commons-io.jar,依賴包。

2.2 fileupload的核心類有

    DiskFileItemFactory、ServletFileUpload、FileItem。
    a、解析原理

2.3 fileupload簡單應用

    使用fileupload組件的步驟如下:
    1. 創建工廠類DiskFileItemFactory對象
        DiskFileItemFactory factory = new DiskFileItemFactory();
    2. 使用工廠創建解析器對象
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
    3. 使用解析器來解析request對象
        List< FileItem > list = fileUpload.parseRequest(request);


    FileItem對象對應一個表單項(表單欄位)。可以是文件欄位普通欄位

    FileItem介面的方法:

        boolean isFormField():判斷當前表單欄位是否為普通文本欄位,如果返回false,則說明是文件欄位
        String getFieldName():獲取欄位名稱,例如:< input type="text" name="username" />,返回的是username。
        String getString():獲取欄位的內容,如果是文件欄位,那麼獲取的是文件內容,當然上傳的文件必須是文本文件。

        String getName():獲取文件欄位的文件名稱(如:a.txt)。
        String getContentType():獲取上傳的文件的MIME類型,例如:text/plain、image/pjpeg。
        int getSize():獲取上傳文件的大小
        InputStream getInputStream():獲取上傳文件對應的輸入流

        void write(File file):把上傳的文件保存到指定文件中。
        void delete();

3、文件上傳時要考慮的幾個問題(經驗分享

a、保證伺服器的安全

    把保存上傳文件的目錄放在用戶直接訪問不到的地方。

b、避免文件被覆蓋

    讓文件名唯一即可。

c、避免同一個文件夾中的文件過多

    方案一:按照日期進行打散存儲目錄


    方案二:用文件名的hashCode計算打散的存儲目錄:二級目錄

d、限制文件的大小:web方式不適合上傳大的文件

    設置單個文件大小
        ServletFileUpload.setFileSizeMax(位元組);
    設置總文件大小:(多文件上傳時)
        ServletFileUpload.setSizeMax(位元組);

e、上傳欄位用戶沒有上傳的問題

    通過判斷文件名是否為空即可。

f、臨時文件的問題

    DiskFileItemFactory
        作用:產生FileItem對象。
        DiskFileItemFactory內部有一個緩存,緩存大小預設是10Kb。如果上傳的文件超過10Kb,就用磁碟作為緩存。
        存放緩存文件的目錄在哪裡?:預設是系統的臨時目錄。

    FileItem.delete();
        FileItem.delete();  如果自己用IO流實現的文件上傳,則要在流關閉後,清理臨時文件。

    FileItem.write(File file);
        把上傳的文件保存到指定文件,該方式會自動刪除臨時文件,註意:實際操作不能夠自動刪除臨時文件(即:使用 FileItem自帶的方法上傳文件)。

    完整的示例代碼如下:

package com.itheima.upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
public class UploadServlet2 extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// request.setCharacterEncoding("UTF-8"); // 解決伺服器接收請求的編碼問題,該方式的優先順序不高,不好!
// 要執行文件上傳的操作
// 首先判斷表單是否支持文件上傳。即:判斷 enctype="multipart/form-data"
boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
if (!isMultipartContent) {
throw new RuntimeException("your form is not multipart/form-data");
}
// 創建一個DiskFileItemfactory工廠類對象
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File("e:\\")); // 指定臨時文件的存儲目錄
// 使用工廠創建解析器ServletFileUpload核心對象
ServletFileUpload sfu = new ServletFileUpload(factory);
// 解決上傳文件表單項出現亂碼問題
sfu.setHeaderEncoding("UTF-8");
// 使用解析器來解析request對象 ,並得到一個表單項的List集合
try {
// 限制上傳文件的大小
// sfu.setFileSizeMax(1024 * 1024 * 3); // 表示3M大小的文件
// sfu.setSizeMax(1024 * 1024 * 6); // 多個文件上傳時
List<FileItem> fileItems = sfu.parseRequest(request);
// 遍歷表單項數據
for (FileItem fileitem : fileItems) {
if (fileitem.isFormField()) {
// 文本表單項,普通文本欄位(字元串)
processFormField(fileitem);
} else {
// 文件表單項,文件欄位(使用位元組流讀取)
processUploadField(fileitem);
}
}
} catch (FileUploadBase.FileSizeLimitExceededException e) {
// throw new RuntimeException("文件過大,不能超過3M");
System.out.println("文件過大,不能超過3M"); // 自定義異常
} catch (FileUploadBase.SizeLimitExceededException e) {
System.out.println("總文件大小不能超過6M"); // 自定義異常
} catch (FileUploadException e) {
e.printStackTrace();
}
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
// 文本表單項,普通文本欄位(字元串)
private void processFormField(FileItem fileitem) {
try {
String fieldname = fileitem.getFieldName(); // 獲取欄位名
String fieldvalue = fileitem.getString("UTF-8"); // 獲取欄位值,並解決上傳普通文本表單出的亂碼問題
// 解決上傳普通文本表單項出現的亂碼問題
// fieldvalue = new String(fieldvalue.getBytes("iso-8859-1"), "utf-8");
System.out.println(fieldname + "=" + fieldvalue);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// 文件表單項,文件欄位(使用位元組流讀取)
private void processUploadField(FileItem fileitem) {
try {
// 得到文件輸入流
InputStream is = fileitem.getInputStream();
// 創建一個文件存檔的目錄
String directoryRealPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File storeDirectory = new File(directoryRealPath); // 既代表文件又代表目錄
if (!storeDirectory.exists()) {
storeDirectory.mkdirs(); // 就創建一個指定的目錄
}
// 獲取文件欄位的文件名稱
String filename = fileitem.getName(); // 文件項框中的值 F:\圖片素材\小清新\43.jpg 或者 43.jpg
// 處理文件名問題 F:\apache-tomcat-7.0.52\webapps\day18_00_upload\ upload\F:\圖片素材\小清新\43.jpg
if (filename != null) {
// filename = filename.substring(filename.lastIndexOf(File.separator) + 1);
filename = FilenameUtils.getName(filename); // 效果同上
}
// 解決文件同名的問題
filename = UUID.randomUUID() + "_" + filename;
// 法一:按日期打散(創建)目錄
// String childDirectory = makeChildDirectory(storeDirectory); // 2015-10-19
// 法二:用文件名的hashCode計算打散的存儲目錄:即二級目錄
String childDirectory = makeChildDirectory(storeDirectory, filename); // a/b
// 在storeDirectory目錄下,創建完整目錄下的文件
File file = new File(storeDirectory, childDirectory + File.separator + filename); // 絕對目錄/日期目錄/文件名
// 通過文件輸出流將上傳的文件保存到伺服器的磁碟
FileOutputStream fos = new FileOutputStream(file);
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
}
fos.close();
is.close();
fileitem.delete(); // 如果自己用IO流實現的文件上傳,則要在流關閉後,清除臨時文件
// 把上傳的文件保存到指定文件(使用 FileItem自帶的方法上傳文件)
// fileitem.write(new File(storeDirectory, childDirectory + File.separator + filename));
// fileitem.delete();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 用文件名的hashCode計算打散的存儲目錄:二級目錄
private String makeChildDirectory(File storeDirectory, String filename) {
int hashcode = filename.hashCode(); // 返回字元串轉換的32位hashcode碼
System.out.println(hashcode);
String code = Integer.toHexString(hashcode); // 把hashcode碼轉換為16進位的字元
System.out.println(code); // 例如:abdsaf2131safsd
String childDirectory = code.charAt(0) + File.separator + code.charAt(1); // 取前兩位,作為二級目錄,例如:a/b
// 創建指定目錄
File file = new File(storeDirectory, childDirectory);
if (!file.exists()) {
file.mkdirs();
}
return childDirectory;
}
/*
// 按日期打散目錄
private String makeChildDirectory(File storeDirectory) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateDirectory = sdf.format(new Date());
// 創建指定目錄
File file = new File(storeDirectory, dateDirectory);
if (!file.exists()) {
file.mkdirs();
}
return dateDirectory;
}*/

}

    一次上傳一個文件的form表單代碼upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form enctype="multipart/form-data"
action="${pageContext.request.contextPath }/servlet/uploadServlet2"
method="post">

<input type="text" name="name" /><br />
<input type="file" name="photo" /><br />
<input type="submit" value="上傳" /><br />
</form>
</body>
</html>

    一次上傳兩個文件的form表單代碼upload2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單例設計模式 目的:讓類創建對象,在系統中只有唯一的實例,讓每一次創建的對象返回的記憶體地址都是相同的。 __new__方法 使用類名創建對象時,python解釋器首先會調用__new__方法為對象分配空間 __new__方法是有object基類提供的靜態方法,主要作用有兩個: 為記憶體中的對象分配空間 ...
  • 最近想系統學習並使用一下boost的asio非同步網路庫,所以需要編譯boost庫使用,下麵簡單介紹如何編譯。 ...
  • 今天介紹的專題是servlet和jsp,屬於web開發中的基礎,先來實際操作一下servlet,創建你的第一個web小項目。 1、新建一個maven項目。 滑鼠右鍵單擊eclipse左邊項目欄的空白區,選擇第一個new,在出現的下拉中選擇other,在新彈出的框中選擇maven,點開maven選擇m ...
  • 很多寫C/C++的人都知道“記憶體對齊”的概念以及規則,但不一定對他有很深入的瞭解。這篇文章試著從硬體到C++語言、更徹底地講一下C++的記憶體對齊。 ...
  • Java開源生鮮電商平臺-銷售管理設計與架構(源碼可下載) 說明:在Java開源生鮮電商平臺中,銷售人員我們稱為跟餐飲店老闆溝通與下載APP的一類地推人員。(所謂地推指的就是一個一個上門拜訪。) 由於銷售人員有以下幾類特性: 1. 時間隨意性,他們並不類似技術或者性質人員,需要天天呆在辦公室,他們是 ...
  • Q: 為什麼要引入鏈表的概念?它是解決什麼問題的? A: 數組作為數據存儲結構有一定的缺陷,在無序數組中,搜索是低效的;而在有序數組中,插入效率又很低;不管在哪一個數組中刪除效率都很低;況且一個數組創建後,它的大小是不可改變的。 A: 在本篇中,我們將學習一種新的數據結構 —— 鏈表,它可以解決上面 ...
  • django rest framework 中的序列化組件,可以說是其核心組件,也是我們平時使用最多的組件,它不僅僅有序列化功能,更提供了數據驗證的功能(與django中的form類似)。 便於展現的序列化操作,我們需要在model添加外鍵、多對多情況。以下是新的models(請刪除原有的資料庫,重 ...
  • 老樣子,拋出個問題,我們想要創建一個實例,但是由於某些原因想繞過__init__方法,用別的方式來進行創建。 舉個慄子 小賤賤反序列化數據,或者說實現一個類方法將其作為備選的構造函數,都屬於這種情況。舉個慄子: 採用下麵的方法可以不用調用__init__()創建一個Date實例: 但是註意,此時使用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...