十、文件上傳

一、文件上傳介紹

要將客戶端(瀏覽器)大數(shù)據(jù)存儲(chǔ)到服務(wù)器端,不將數(shù)據(jù)直接存儲(chǔ)到數(shù)據(jù)庫(kù)中,而是要將數(shù)據(jù)存儲(chǔ)到服務(wù)器所在的磁盤上,這就要使用文件上傳。
作用:減少了數(shù)據(jù)庫(kù)服務(wù)器的壓力,對(duì)數(shù)據(jù)的操作更加靈活。

二、文件上傳原理分析

所謂的文件上傳就是服務(wù)器端通過request對(duì)象獲取輸入流,將瀏覽器端上傳的數(shù)據(jù)讀取出來,保存到服務(wù)器端
瀏覽器端操作
請(qǐng)求方式必須是post
使用<input type=’file’ name=’xxx’>,必須有name屬性且有值
表單必須設(shè)置encType=”multipart/form-data”

服務(wù)器端操作
通過request對(duì)象,獲取inputStream,就可以將瀏覽器提交的所有數(shù)據(jù)讀取到.
注意:
這時(shí)候,獲取其他上傳參數(shù)的時(shí)候,通過request.getParameter(“”)獲取不到了.

三、Commons-fileupload

commons-fileupload介紹
Apache 開源組織提供了一個(gè)用來處理表單文件上傳的一個(gè)開源組件( Commons-fileupload ),該組件性能優(yōu)異,并且其API使用極其簡(jiǎn)單,可以讓開發(fā)人員輕松實(shí)現(xiàn)web文件上傳功能,因此在web開發(fā)中實(shí)現(xiàn)文件上傳功能,通常使用Commons-fileupload組件實(shí)現(xiàn)。

使用Commons-fileupload組件實(shí)現(xiàn)文件上傳,需要導(dǎo)入該組件相應(yīng)的支撐jar包:
Commons-fileupload和commons-io。

commons-io 不屬于文件上傳組件的開發(fā)jar文件,但Commons-fileupload 組件從1.1 版本開始,它工作時(shí)需要commons-io包的支持。

commons-fileupload API簡(jiǎn)單介紹
DiskFileItemFactory,用于設(shè)置緩存大小及臨時(shí)文件存儲(chǔ)位置
ServletFileUpload,真正用于文件上傳的核心類
FIleItem,代表的是一個(gè)上傳項(xiàng)

入門案例
在瀏覽器端創(chuàng)建一個(gè)可以上傳文件的表單,在服務(wù)器端通過commons-fileupload完成文件上傳。

瀏覽器端注意三件事情:
1.表單的提交方式為post
2.在表單上添加屬性 encType=”multipart/form-data”
3.使用<input type=’file’ >,添加name屬性且有值

服務(wù)器端:
創(chuàng)建DiskFileItemFactory
創(chuàng)建ServletFileUpload
通過ServletFileUpload的parseRequest方法得到所有的FileItem

瀏覽器端

<form action="${pageContext.request.contextPath}/upload" method="post"  enctype="multipart/form-data">
        <input type="file" name="f"> 
        <input type="submit" value="上傳">
</form>

服務(wù)器端

// 1.創(chuàng)建一個(gè) DiskFileItemFactory
    DiskFileItemFactory factory = new DiskFileItemFactory();

    // 2.創(chuàng)建一個(gè)ServletFileUpload
    ServletFileUpload upload = new ServletFileUpload(factory);

    // 3.完成上傳操作

    try {
        // 3.1 得到所有上傳項(xiàng)
        List<FileItem> items = upload.parseRequest(request);
        // 3.2遍歷上傳項(xiàng)
        for (FileItem item : items) {
            // 3.3判斷是否是上傳組件
            if (!item.isFormField()) {
                // 3.4 將文件真正上傳
                IOUtils.copy(item.getInputStream(), new FileOutputStream(
                        "f:/upload/a.txt"));
            }
        }
    } catch (FileUploadException e) {
        e.printStackTrace();
    }

1. Commons-fileupload詳解-DiskFileItemFactory
設(shè)置緩存大小
factory.setSizeThreshold(1024*1024); //設(shè)置為1m 默認(rèn)是10k

設(shè)置臨時(shí)文件存儲(chǔ)位置
File temp=new File(this.getServletContext().getRealPath("/temp"));
factory.setRepository(temp); //可以指定臨時(shí)文件存儲(chǔ)位置,默認(rèn)是系統(tǒng)的臨時(shí)文件存儲(chǔ)位置

2. Commons-fileupload詳解-ServletFileUpload
parseRequest方法
List<FileItem> pareRequest(HttpServletRequest request)
得到所有的上傳信息,將每一部分映射成FileItem對(duì)象.

isMultipartContent方法
boolean isMultipartContent(HttpServletRequest request)
這個(gè)方法返回值是boolean,它是用于判斷當(dāng)前表單是否是一個(gè)上傳的表單,
簡(jiǎn)單說,就判斷它的encType的值是否是 multipart/form-data.

setHeaderEncoding方法
用于解決上傳文件名稱中文亂碼問題

設(shè)置上傳文件大小
void setFileSizeMax(long fileSizeMax) 設(shè)置單個(gè)文件上傳大小
void setSizeMax(long sizeMax) 設(shè)置總文件上傳大小

3. Commons-fileupload詳解-FileItem
isFormField方法
這個(gè)方法返回的是boolean類型,
它是判斷當(dāng)前組件是否是上傳組件 簡(jiǎn)單說,就是判斷type="file",
如果返回true,代表不是上傳組件,返回false,代表是上傳組件

getName方法
獲取上傳組件的上傳文件的名稱,如果是非上傳組件,返回的是null

getFieldName方法
獲取組件名稱,簡(jiǎn)單說,就是表單的元素的name值。

getString方法
獲取非上傳組件的value值,
通過它也可以獲取上傳的文件內(nèi)容,但是,使用它獲取不合適。如果使用了commons-fileupload進(jìn)行文件上傳,而上傳表單中包含了 非上傳組件,獲取其值,不能使用request獲取.getString()有一個(gè)重載的方法
getString(String encoding)可以解決亂碼問題

getInputStream方法
通過FileItem.getInputStream();可以獲取一個(gè)輸入流,這個(gè)輸入流就可以讀取出上傳文件內(nèi)容。

    InputStream is = item.getInputStream(); // 用于讀取上傳文件內(nèi)容的輸入流.
    FileOutputStream fos = new FileOutputStream("f:/upload/" + filename);
    int len = -1;
    byte[] b = new byte[1024 * 10];
    while ((len = is.read(b)) != -1) {
        fos.write(b, 0, len);
        fos.flush();
    }
    fos.close();
    is.close();

可以使用IOUtils工具完成文件copy操作
IOUtils.copy(is, fos);

delete方法
它是用于上傳完成后,刪除臨時(shí)文件的

四、 多文件上傳

我們?cè)趯戉]件中可以添加多個(gè)附件,那么我們?cè)谖募蟼鲿r(shí),是不是也可以上傳多個(gè)文件哪,答案是一定的,那么怎樣實(shí)現(xiàn)多個(gè)文件上傳哪?
我們可以通過js實(shí)現(xiàn)瀏覽器端的上傳文件框的動(dòng)態(tài)添加。服務(wù)器端代碼不需要改變。

function addFile() {
    //1.得到id叫content的div
    var div=document.getElementById("content"); 
    //2.向其中添加一段html代碼
div.innerHTML+="<div><input type='file' name='f'><input type='button' value='remove File' onclick='removeFile(this);'></div>";
}
function removeFile(btn){
    document.getElementById("content").removeChild(btn.parentNode);
}

五、文件上傳問題

1. 文件重名

每一個(gè)客戶端都可以進(jìn)行文件上傳操作,那么當(dāng)我們上傳的文件過多,一定會(huì)出現(xiàn)同名的文件,那么在服務(wù)器端只能保存一個(gè),對(duì)于這個(gè)問題,我們?cè)谏蟼魑募r(shí),就需要考慮文件重名問題.
一般情況下,對(duì)于上傳文件,為了保證不重名,會(huì)給文件起一個(gè)隨機(jī)名.
一種方案是使用uuid.
一種方案是使用毫秒值

2. 存儲(chǔ)位置

本質(zhì)就是上傳的文件是否允許瀏覽器端直接訪問。
例如:商品添加時(shí)需要一個(gè)圖片,這個(gè)圖片一定是可以直接被瀏覽器端訪問的。

允許被瀏覽器端訪問:放置在WebRoot下,但不能是WEB-INF或META-INF下其及子目錄下
不允許被瀏覽器端訪問:
若放在工程下則放置在WEB-INF或META-INF及其子目錄下.
也可以不放在工程下

3. 目錄分離

當(dāng)我們上傳文件過多,并且保存在同一個(gè)目錄下時(shí),我們就需要考慮怎樣處理它們,因?yàn)橐粋€(gè)目錄下文件過多,不僅降低性能,操作時(shí)也不方便。
為了防止同一個(gè)目錄下方上傳文件數(shù)量過多

  1. 按照上傳時(shí)間進(jìn)行目錄分離 (周、月 )
  2. 按照上傳用戶進(jìn)行目錄分離 ----- 為每個(gè)用戶建立單獨(dú)目錄
  3. 按照固定數(shù)量進(jìn)行目錄分離 ------ 假設(shè)每個(gè)目錄只能存放3000個(gè)文件 ,每當(dāng)一個(gè)目錄存滿3000個(gè)文件后,創(chuàng)建一個(gè)新的目錄
  4. 按照唯一文件名的hashcode 進(jìn)行目錄分離
  public static String generateRandomDir(String uuidFileName) {
        // 獲得唯一文件名的hashcode
        int hashcode = uuidFileName.hashCode();
        // 獲得一級(jí)目錄
        int d1 = hashcode & 0xf;       
        // 獲得二級(jí)目錄
        int d2 = (hashcode >>> 4) & 0xf;
        return "/" + d2 + "/" + d1;// 共有256目錄
    }

六、案例:使用Servlet3.0技術(shù)完成文件的上傳

技術(shù)分析:
【Servlet3.0】

Servlet3.0 與 Servlet2.5:

 * Servlet3.0需要運(yùn)行在tomcat7以上的服務(wù)器中.

 * Servlet3.0以后web.xml就不是必須的.

1.Servlet3.0支持注解開發(fā).

2.支持文件上傳.

【Servlet3.0支持注解開發(fā)】

使用@WebServlet替換web.xml中配置的Servlet:

@WebServlet(urlPatterns="/ServletDemo1",loadOnStartup=2,initParams=@WebInitParam(name="username",value="root"))

使用@WebListener替換web.xml中監(jiān)聽器的配置:

@WebListener

[使用@WebFilter替換web.xml中的過濾器的配置:](mailto:使用@WebFilter替換web.xml中的過濾器的配置:)

@WebFilter(urlPatterns="/*")

【Servlet3.0的文件上傳】
文件上傳:
文件上傳:指的是將本地的文件 寫到 服務(wù)器上.

文件上傳的要素:
1.表單的提交的方式必須是POST.
2.表單中必須有一個(gè)文件上傳項(xiàng):<input type=”file”>,而且文件上傳項(xiàng)必須有name屬性和值.
<input type=”file” name=”upload”/>
3.表單的enctype屬性的值必須是multipart/form-data

文件上傳的抓包分析:
未修改enctype屬性的時(shí)候:

POST /WEB17_WEB/demo1/demo1.jsp HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 22325-10011
Referer: http://localhost:8080/WEB17_WEB/demo1/demo1.jsp
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 47
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=99CD51DA9A47D29200168968AD983E9E

upload=C%3A%5CUsers%5Capple%5CDesktop%5Caaa.txt

已經(jīng)修改了enctype屬性:

POST /WEB17_WEB/demo1/demo1.jsp HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 22325-10026
Referer: http://localhost:8080/WEB17_WEB/demo1/demo1.jsp
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e02e526160b66
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 224
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=99CD51DA9A47D29200168968AD983E9E

-----------------------------7e02e526160b66
Content-Disposition: form-data; name="upload"; filename="C:\Users\apple\Desktop\aaa.txt"
Content-Type: text/plain

Hello shouyi
-----------------------------7e02e526160b66—

【文件上傳的原理】
根據(jù)分割線將請(qǐng)求體的部分分成幾塊:
判斷 每塊是 普通項(xiàng)還是文件上傳項(xiàng).
普通項(xiàng):獲得名稱和值.
文件上傳項(xiàng):獲得文件名 和 文件內(nèi)容輸入流.

【文件上傳的技術(shù)】
JspSmartUpload: jspSmartUpload組件是應(yīng)用JSP進(jìn)行B/S程序開發(fā)過程中經(jīng)常使用的上傳下載組件,它使用簡(jiǎn)單,方便?,F(xiàn)在我又為其加上了下載中文名字的文件的支持,真?zhèn)€是如虎添翼,必將贏得更多開發(fā)者的青睞。-Model1年代的文件上傳的工具.
FileUpload 是 Apache commons下面的一個(gè)子項(xiàng)目,用來實(shí)現(xiàn)Java環(huán)境下面的文件上傳功能,與常見的SmartUpload齊名.應(yīng)用在Model2年代了.

代碼實(shí)現(xiàn):

/**
 * 文件上傳的Servlet
 */
@WebServlet("/UploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 接收普通項(xiàng):
        request.setCharacterEncoding("UTF-8");
        String desc = request.getParameter("desc");
        System.out.println("文件描述:"+desc);
        Part part = request.getPart("upload");
        // 獲得上傳的文件的大小
        long size = part.getSize();
        System.out.println("文件大小"+size);
        String type = part.getContentType();
        System.out.println("文件類型"+type); // text/plain  image/jpeg 
        String name = part.getName();
        System.out.println(name); 
        // 獲得文件名:
        String header = part.getHeader("Content-Disposition");
        System.out.println(header);
        
        int idx = header.lastIndexOf("filename=\"");
        String fileName = header.substring(idx+10, header.length()-1);
        System.out.println(fileName);
        
        // 獲得文件內(nèi)容:
        InputStream is = part.getInputStream();
        
        // 獲得文件上傳路徑:
        String path = this.getServletContext().getRealPath("/upload");
        
        OutputStream os = new FileOutputStream(path+"/"+fileName);
        int len = 0;
        byte[] b = new byte[1024];
        while((len = is.read(b))!=-1){
            os.write(b, 0, len);
        }
        is.close();
        os.close();
        
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文包括:1、文件上傳概述2、利用 Commons-fileupload 組件實(shí)現(xiàn)文件上傳3、核心API——Dis...
    廖少少閱讀 12,744評(píng)論 5 91
  • 一、文件上傳概述 實(shí)現(xiàn)web開發(fā)中的文件上傳功能,需完成如下二步操作在web頁(yè)面中添加上傳輸入項(xiàng)在servlet中...
    yjaal閱讀 3,004評(píng)論 0 22
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,552評(píng)論 19 139
  • 出生入死 人生的問題,林林總總,似乎說不清也道不盡。實(shí)際上,人生最根本的問題只有一個(gè),那便是生死。弄明白了生死問題...
    金融世界閱讀 1,109評(píng)論 0 4
  • 錄音有一個(gè)不好的地方在于,你花了多長(zhǎng)時(shí)間去錄音,那么對(duì)方就需要花多少的時(shí)間來進(jìn)行聽,比如說花了1000個(gè)小時(shí)錄音,...
    劍飛在思考閱讀 385評(píng)論 0 0

友情鏈接更多精彩內(nèi)容