java web文件上傳

什么是文件上傳

要將客戶端(瀏覽器)大數(shù)據(jù)存儲(chǔ)到服務(wù)器端,不將數(shù)據(jù)直接存儲(chǔ)到數(shù)據(jù)庫中,
而是要將數(shù)據(jù)存儲(chǔ)到服務(wù)器所在的磁盤上,這要使用文件上傳

為什么使用文件上傳

通過文件上傳,可以將瀏覽器端的大數(shù)據(jù)直接保存到服務(wù)器端金度。
不將數(shù)據(jù)保存到數(shù)據(jù)庫中,而是保存到服務(wù)器磁盤上严沥,這樣減少了數(shù)據(jù)庫服務(wù)器的壓力猜极,
對(duì)數(shù)據(jù)的操作更加靈活

文件上傳原理分析

所謂的文件上傳就是服務(wù)器端通過request對(duì)象獲取輸入流,
將瀏覽器端上傳的數(shù)據(jù)讀取出來消玄,保存到服務(wù)器端跟伏。

上傳文件注意事項(xiàng)

  • 1,請(qǐng)求方式必須是post.
  • 2,需要使用組件 <input type="file" name="f"> 組件必須有名稱.
  • 3莱找,表單必須設(shè)置encType="multipart/form-data".

服務(wù)器端處理

通過request對(duì)象酬姆,獲取InputStream,可以將瀏覽器提交的所有數(shù)據(jù)讀取到奥溺。
<form action="${pageContext.request.contextPath }/servlet/upload3"
    method="post" enctype="multipart/form-data">
    <p>
        用戶名:<input type="text" name="name">
    </p>
    <p>
        文件:<input type="file" name="photo">
    </p>
    <input type="submit" value="上傳">
</form> 

InputStream is = request.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len =is.read(buffer)) !=-1){
    System.out.println(new String(buffer,0,len));
}
is.close();

------WebKitFormBoundaryT3WBozNnn4BoAsmx
Content-Disposition: form-data; name="name"

李浩
------WebKitFormBoundaryT3WBozNnn4BoAsmx
Content-Disposition: form-data; name="photo"; filename="aina_cert.sql"
Content-Type: application/octet-stream


------WebKitFormBoundaryT3WBozNnn4BoAsmx--

fileupload 實(shí)現(xiàn)文件上傳,接收用戶上傳的文件

fileupload 是什么

fileupload是由apache的commons組件提供上傳組件骨宠。它最主要的工作就是幫我們解析
request.getnputStram().
導(dǎo)入commons-fileupload 相關(guān) jar包
    * commons-fileupload.jar 核心包
    * commons-io.jar 依賴包

fileupload 有什么用

form表單encType="multipart/form-data" 上傳文件時(shí)幫助我們 處理表單數(shù)據(jù)浮定。

fileupload 核心類

DiskFileItemFactory ServletFileUpload  FileItem

DiskFileItemFactory
  • 設(shè)置緩存大小
    factory.setSizeThreshold(1024*1024) 設(shè)置為1m默認(rèn)是10k
  • 設(shè)置臨時(shí)文件存儲(chǔ)位置
    File temp = new File(this.getServletContext().getRealPath("temp"));
    //可以指定臨時(shí)文件存儲(chǔ)位置,默認(rèn)是系統(tǒng)的臨時(shí)文件存儲(chǔ)位置
    factory.setRepository(temp);

ServletFileUpload
  • parseRequest方法
    //得到所有的上傳信息层亿,將每一部分映射成FileItem對(duì)象
    List<Fileitme> upload.pareRequest(request)

  • isMultipartContent 方法
    boolean isMultipartContent(request)
    這個(gè)方法返回值是boolean 它是用于判斷當(dāng)前表單是否是一個(gè)上傳的表單桦卒,簡單說
    就判斷它的encType的值是否是mulipart/form-data。

  • setHeaderEncoding 方法
    用于解決上傳文件/表單 中文亂碼問題

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


FileItem
  • boolean isFormField() 判斷當(dāng)前表單字段是否為普通文本字段匿又,如果返回false,說明是文件字段方灾;

  • String getFieldName() 獲取字段名稱:<input type="text" name="username"> 是指username.

  • String getString("utf-8") 獲取字段的內(nèi)容,如果是文件字段碌更,那么獲取的文件內(nèi)容裕偿,當(dāng)然上傳的文件必須是文本文件;

  • String getName():獲取文件字段名稱(xxx.png)

  • String getContextType():獲取上傳的文件mime類型痛单,例如 text/plain.

  • int getSize() 獲取上傳文件的大小

  • InputStream getInputStream() 獲取上傳文件對(duì)應(yīng)的輸入流嘿棘。

  • void write(File):把上傳的文件保存到指定文件中

  • delete() 刪除臨時(shí)文件


使用步驟
  • 1,創(chuàng)建工廠類DiskFileItemFactory 對(duì)象:
    DiskFileItemFactory factory = new DiskFileItemFactory()
  • 2旭绒,使用工廠創(chuàng)建解析器對(duì)象
    ServletFileUpload fileUpload = new ServletFileUpload(factory);
  • 3,使用解析器來解析request對(duì)象
    List<FileItem> list = fileUpload.parseRequest(request);
//判斷表單是否支持文件鸟妙。即enctype="multipart/form-data"
boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
if(!isMultipartContent) {
    throw new RuntimeException("your form is not multipart/form-data");
}

//創(chuàng)建工廠類DiskFileItemFactory 對(duì)象
DiskFileItemFactory factory = new DiskFileItemFactory();
//創(chuàng)建一個(gè)ServletFileUpload核心對(duì)象
ServletFileUpload sfu = new ServletFileUpload(factory);
try {
    List<FileItem> fileItems = sfu.parseRequest(request);
    for(FileItem item:fileItems) {
        if(item.isFormField()) { //true 是普通字段 false 文件字段
            processFormField(item);
        } else{
            processUploadField(item);
        }
    }
} catch (FileUploadException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}   

/**
 * 上傳文件
 * @param item
 */
private void processUploadField(FileItem fileitem) {
    //得到文件輸入流
    try {
        InputStream is = fileitem.getInputStream();
        //項(xiàng)目目錄
        String directionRealPath = path+File.separator+"upload";
        File storeDirectory = new File(directionRealPath);
        if(!storeDirectory.exists()) {
            storeDirectory.mkdirs();
        }
        //文件名稱
        File savefile = new File(storeDirectory,fileitem.getName());
        fileitem.write(savefile);
        fileitem.delete();
    } catch (Exception e) {
        e.printStackTrace();
    }
    
}

上傳文件時(shí)要考慮的問題

1焦人,允許被瀏覽器端訪問:放置在WebRoot 下,但不能是WEB-INF或META-INF下其子目錄下
2重父,不允許被瀏覽器端訪問: 放置在WEB-INF或META-INF

保證服務(wù)器的安全
把保存上傳文件的目錄放在用戶直接訪問不到的地方(瀏覽器可以訪問到的地方)花椭。
避免文件被覆蓋
讓文件名唯一即可
`filename = UUID.randomUUID() + "_" + filename; `
避免同一個(gè)文件夾中的文件過多

####### 按照日期進(jìn)行打散存儲(chǔ)目錄
private File makeChildDirectory(File storeDirectroy) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateDir = sdf.format(new Date());
File file = new File(storeDirectroy,dateDir);
if(!file.exists()) {
file.mkdirs();
}
return file;
}

####### 用文件名的hashCode計(jì)算打算的存儲(chǔ)目錄:二級(jí)目錄
private File makeChildDirctory(File storeDirectroy,String fileName) {
int hashCode = fileName.hashCode();
//轉(zhuǎn)換成16進(jìn)制
String hex = Integer.toHexString(hashCode);
String childDirctory = hex.charAt(0)+File.separator+hex.charAt(1);
File childDirctoryFile = new File(storeDirectroy,childDirctory);
if(!childDirctoryFile.exists()) {
childDirctoryFile.mkdirs();
}
return childDirctoryFile;

}

####### 完整示例
public class Upload3Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private String path = "/Users/lihao/Downloads/apache-tomcat-8.0.37/webapps/bookmanager/WebContent";

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

        boolean isMultipart = ServletFileUpload.isMultipartContent(request); 
        if (!isMultipart) {
            throw new RuntimeException("your form is not multipart/form-data");
        }

        // 設(shè)置文件大小位置等等
        DiskFileItemFactory diskfactory = new DiskFileItemFactory();
        //指定臨時(shí)文件的存儲(chǔ)目錄
//      diskfactory.setRepository(new File("/Users/lihao/"));
        // 核心類處理上傳邏輯
        ServletFileUpload fileUpload = new ServletFileUpload(diskfactory);
        
        //解決表單亂碼
        fileUpload.setHeaderEncoding("utf-8");
        //進(jìn)度條
        fileUpload.setProgressListener(new ProgressListener() {
            @Override
            public void update(long pBytesRead, long pContentLength, int pItems) {
            
                   if (pContentLength == -1) {
                       System.out.println("文件錯(cuò)誤");
                   } else {
                        float percent = (float) (pBytesRead*1.0/pContentLength*100);
                        int percents = (int) percent;
                       System.out.println("已完成: "+percents+"%");
                   }
            }
        });

        try {
//          fileUpload.setFileSizeMax(1024*1024*3); //單個(gè)文件不能超過3M
//          fileUpload.setSizeMax(1024*1024*6);//總上傳文件不能超過6M
            List<FileItem> listItem = fileUpload.parseRequest(request);
            for (FileItem item : listItem) {
                if (item.isFormField()) {
                    // 普通文本input
                    processFormField(item);
                } else {
                    // 文件上傳
                    if(item.getName() != null && !item.getName().equals("")){
                        //擴(kuò)展名
                        System.out.println("擴(kuò)展名:"+FilenameUtils.getExtension(item.getName()));
                    
                        processFileUpload(item);
                    } else {
                        System.out.println("沒有提交文件");
                    }
                    
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }

    }

    //普通input 
    private void processFormField(FileItem item) {
        try {
            System.out.println(item.getFieldName() +"  "+ item.getString("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        
    }

    // 上傳文件
    private void processFileUpload(FileItem item) {
        String directory = path + File.separator + "upload";
        // upload file name
        // f:xxx\sf.png
        String filename = item.getName();

        if (filename != null) {
            filename = FilenameUtils.getName(filename);
        }
        // 解決重名問題
         filename = UUID.randomUUID() + "_" + filename;

        File dirctoryFile = new File(directory);
        if (!dirctoryFile.exists()) {
            dirctoryFile.mkdirs();
        }
        
        //按照日期進(jìn)行打算存儲(chǔ)目錄
//      File newFileDir = makeChildDirectory(dirctoryFile);
        //用文件名的hashCode計(jì)算打散的存儲(chǔ)目錄:二級(jí)目錄
        File newFileDir = makeChildDirctory(dirctoryFile,filename);
        
        try {
            item.write(new File(newFileDir, filename));
            //刪除臨時(shí)文件
            item.delete();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    /**
     * 按照日期打散目錄
     * @param storeDirectroy
     * @return
     */
    private File makeChildDirectory(File storeDirectroy) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String dateDir = sdf.format(new Date());
        File file = new File(storeDirectroy,dateDir);
        if(!file.exists()) {
            file.mkdirs();
        }
        return  file;
    }
    
    /**
     * 文件名的hashCode 計(jì)算打散的存儲(chǔ)目錄:二級(jí)目錄
     * @param storeDirectroy
     * @param fileName
     * @return
     */
    private File makeChildDirctory(File storeDirectroy,String fileName) {
        int hashCode = fileName.hashCode();
        //轉(zhuǎn)換成16進(jìn)制
        String hex = Integer.toHexString(hashCode);
        String childDirctory =  hex.charAt(0)+File.separator+hex.charAt(1);
        File childDirctoryFile = new File(storeDirectroy,childDirctory);
        if(!childDirctoryFile.exists()) {
            childDirctoryFile.mkdirs();
        }
        return childDirctoryFile;
        
    }

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

}   

<form action="${pageContext.request.contextPath }/servlet/upload3"
    method="post" enctype="multipart/form-data">
    <p>
        用戶名:<input type="text" name="name">
    </p>
    <p>
        文件:<input type="file" name="photo">
    </p>
    <input type="submit" value="上傳">
</form>

2016.11.10

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市房午,隨后出現(xiàn)的幾起案子矿辽,更是在濱河造成了極大的恐慌,老刑警劉巖歪沃,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗦锐,死亡現(xiàn)場離奇詭異,居然都是意外死亡沪曙,警方通過查閱死者的電腦和手機(jī)奕污,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來液走,“玉大人碳默,你說我怎么就攤上這事≡悼簦” “怎么了嘱根?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長巷懈。 經(jīng)常有香客問我该抒,道長,這世上最難降的妖魔是什么顶燕? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任凑保,我火速辦了婚禮,結(jié)果婚禮上涌攻,老公的妹妹穿的比我還像新娘欧引。我一直安慰自己,他們只是感情好恳谎,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布芝此。 她就那樣靜靜地躺著,像睡著了一般因痛。 火紅的嫁衣襯著肌膚如雪婚苹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天婚肆,我揣著相機(jī)與錄音租副,去河邊找鬼。 笑死较性,一個(gè)胖子當(dāng)著我的面吹牛用僧,可吹牛的內(nèi)容都是我干的结胀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼责循,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼糟港!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起院仿,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤秸抚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后歹垫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剥汤,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年排惨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吭敢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暮芭,死狀恐怖鹿驼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辕宏,我是刑警寧澤畜晰,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站瑞筐,受9級(jí)特大地震影響凄鼻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聚假,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一野宜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧魔策,春花似錦、人聲如沸河胎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽游岳。三九已至政敢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胚迫,已是汗流浹背喷户。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留访锻,地道東北人褪尝。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓闹获,卻偏偏與公主長得像,于是被迫代替她去往敵國和親河哑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子避诽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文包括:1、文件上傳概述2璃谨、利用 Commons-fileupload 組件實(shí)現(xiàn)文件上傳3沙庐、核心API——Dis...
    廖少少閱讀 12,531評(píng)論 5 91
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)佳吞,斷路器拱雏,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 一铸抑、文件上傳概述 實(shí)現(xiàn)web開發(fā)中的文件上傳功能,需完成如下二步操作在web頁面中添加上傳輸入項(xiàng)在servlet中...
    yjaal閱讀 2,885評(píng)論 0 22
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法花盐,類相關(guān)的語法羡滑,內(nèi)部類的語法,繼承相關(guān)的語法算芯,異常的語法柒昏,線程的語...
    子非魚_t_閱讀 31,598評(píng)論 18 399
  • 馬上要過圣誕節(jié)了,這是珀斯當(dāng)?shù)厝艘荒曛凶钪匾墓?jié)日熙揍。我認(rèn)為完全等同于咱們的春節(jié)职祷,人們?cè)撔菁俚男菁伲摯蟛少彽拇蟛少?..
    西澳朵朵閱讀 167評(píng)論 3 2