1 文件上傳表單
1)上傳文件的本質(zhì)是文本復制的過(guò)程
2)技術(shù)層面,在Java中一定會(huì )用到IO操作,主要以二進(jìn)制方式讀寫(xiě)
3)傳統方式下,對于上傳文件字段不同的瀏覽器有著(zhù)不同的解析方式,例如:
IE6:upfile=c:\aa\bb\a.JPG
非IE6: upfile=a.JPG
4)可以將form以MIME協(xié)議的方式將上傳文件傳遞到服務(wù)端,服務(wù)端以二進(jìn)制流的方式讀寫(xiě)
代碼:客戶(hù)端form enctype="multipart/form-data"/>
服務(wù)端request.getInputStream()
Tip:文件上傳概述
實(shí)現web開(kāi)發(fā)中的文件上傳功能,需完成如下二步操作:
·在web頁(yè)面中添加上傳輸入項
·在servlet中讀取上傳文件的數據,并保存到本地硬盤(pán)中。
如何在web頁(yè)面中添加上傳輸入項?
·<input type=“file”>標簽用于在web頁(yè)面中添加文件上傳輸入項,設置文件上傳輸入項時(shí)須注意:
·1、必須要設置input輸入項的name屬性,否則瀏覽器將不會(huì )發(fā)送上傳文件的數據。
·2、必須把form的enctype屬值設為multipart/form-data.設置該值后,瀏覽器在上傳文件時(shí),將把文件數據附帶在http請求消息體中,并使用MIME協(xié)議對上傳的文件進(jìn)行描述,以方便接收方對上傳數據進(jìn)行解析和處理。
如何在Servlet中讀取文件上傳數據,并保存到本地硬盤(pán)中?
·request對象提供了一個(gè)getInputStream方法,通過(guò)這個(gè)方法可以讀取到客戶(hù)端提交過(guò)來(lái)的數據。但由于用戶(hù)可能會(huì )同時(shí)上傳多個(gè)文件,在servlet端編程直接讀取上傳數據,并分別解析出相應的文件數據是一項非常麻煩的工作,示例。
·為方便用戶(hù)處理文件上傳數據,Apache 開(kāi)源組織提供了一個(gè)用來(lái)處理表單文件上傳的一個(gè)開(kāi)源組件( Commons-fileupload ),該組件性能優(yōu)異,并且其API使用極其簡(jiǎn)單,可以讓開(kāi)發(fā)人員輕松實(shí)現web文件上傳功能,因此在web開(kāi)發(fā)中實(shí)現文件上傳功能,通常使用commons-fileupload組件實(shí)現。
使用commons-fileupload組件實(shí)現文件上傳,需要導入該組件相應的支撐jar包: commons-fileupload和commons-io。commons-io 不屬于文件上傳組件的開(kāi)發(fā)jar文件,但commons-fileupload 組件從1.1 版本開(kāi)始,它工作時(shí)需要commons-io包的支持。
Tip:fileupload組件工作流程

Tip:核心API—DiskFileItemFactory
DiskFileItemFactory 是創(chuàng )建 FileItem 對象的工廠(chǎng),這個(gè)工廠(chǎng)類(lèi)常用方法:
·public void setSizeThreshold(int sizeThreshold)
·設置內存緩沖區的大小,默認值為10K。當上傳文件大于緩沖區大小時(shí), fileupload組件將使用臨時(shí)文件緩存上傳文件。
·public void setRepository(java.io.File repository)
·指定臨時(shí)文件目錄,默認值為System.getProperty("java.io.tmpdir").
·public DiskFileItemFactory(int sizeThreshold, java.io.File repository)
·構造函數
Tip:核心API—ServletFileUpload
ServletFileUpload 負責處理上傳的文件數據,并將表單中每個(gè)輸入項封裝成一個(gè) FileItem 對象中。常用方法有:
·boolean isMultipartContent(HttpServletRequest request)
·判斷上傳表單是否為multipart/form-data類(lèi)型
·List parseRequest(HttpServletRequest request)
·解析request對象,并把表單中的每一個(gè)輸入項包裝成一個(gè)fileItem 對象,并返回一個(gè)保存了所有FileItem的list集合。
·setFileSizeMax(long fileSizeMax)
·設置上傳文件的最大值(單位字節)
·setSizeMax(long sizeMax)
·設置上傳文件總量的最大值
·setHeaderEncoding(java.lang.String encoding)
·設置編碼格式,解決上傳中文名文件的問(wèn)題
Tip:文件上傳案例
實(shí)現步驟
1、創(chuàng )建DiskFileItemFactory對象,設置緩沖區大小和臨時(shí)文件目錄
2、使用DiskFileItemFactory 對象創(chuàng )建ServletFileUpload對象,并設置上傳文件的大小限制。
3、調用ServletFileUpload.parseRequest方法解析request對象,得到一個(gè)保存了所有上傳內容FileItem的List對象。
4、對list進(jìn)行迭代,每迭代一個(gè)FileItem對象,調用其isFormField方法判斷是否是上傳文件
·為普通表單字段,則調用getFieldName、getString方法得到字段名和字段值
·為上傳文件,則調用getName 、 getInputStream方法得到數據輸入流,從而讀取上傳數據。
Tip:上傳文件的處理細節
中文文件名亂碼問(wèn)題
·文件名中文亂碼問(wèn)題,可調用ServletFileUpload的setHeaderEncoding方法,或者設置request的setCharacterEncoding屬性
臨時(shí)文件的刪除問(wèn)題
·由于文件大小超出DiskFileItemFactory.setSizeThreshold方法設置的內存緩沖區的大小時(shí),commons-fileupload組件將使用臨時(shí)文件保存上傳數據,因此在程序結束時(shí),務(wù)必調用FileItem.delete方法刪除臨時(shí)文件。
delete方法的調用必須位于流關(guān)閉之后,否則會(huì )出現文件占用,而導致刪除失敗的情況。
文件存放名字
·為防止多用戶(hù)上傳相同文件名的文件,而導致文件覆蓋的情況發(fā)生,文件上傳程序應保證上傳文件具有唯一文件名。
文件存放位置
·為保證服務(wù)器安全,上傳文件應保存在應用程序的WEB-INF目錄下,或者不受WEB服務(wù)器管理的目錄。
·為防止單個(gè)目錄下文件過(guò)多,影響文件讀寫(xiě)速度,處理上傳文件的程序應根據可能的文件上傳總量,選擇合適的目錄結構生成算法,將上傳文件分散存儲到不同的目錄。
<%@ page language="java" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <script type="text/javascript"> //全局變量 var time = 0; function addLine(addButton){ //創(chuàng )建內部div對象 var divElement = document.createElement("div"); //創(chuàng )建input對象[file類(lèi)型] var inputElement1 = document.createElement("input"); inputElement1.type="file"; inputElement1.name="upfile"; //創(chuàng )建input對象[button類(lèi)型] var inputElement2 = document.createElement("input"); inputElement2.type="button"; inputElement2.value="刪除"; //對刪除按鈕添加事件監聽(tīng) inputElement2.onclick=function(){ //取得該按鈕所在行的直接父元素 var divElement = this.parentNode.parentNode; //通過(guò)父元素刪除直接子元素 divElement.removeChild(this.parentNode); time--; if(time < 5){ //按鈕生效 addButton.disabled=false; //addButton.style.visibility="visible"; } } //依次將file類(lèi)型和button類(lèi)型的input對象加入到內部div對象中 divElement.appendChild(inputElement1); divElement.appendChild(inputElement2); //再次內部div對象加入到外部div對象 var outDivElement = document.getElementById("outDiv"); outDivElement.appendChild(divElement); time++; if(time == 5){ //將按鈕失效 addButton.disabled=true; //addButton.style.visibility="hidden"; } } </script> </head> <body> <form action="${pageContext.request.contextPath}/UploadServlet" method="POST" enctype="multipart/form-data"> <table border="1" align="center"> <caption>文件上傳</caption> <tr> <th>上傳用戶(hù)</th> <td><input type="text" name="username"/></td> </tr> <tr> <th></th> <td> <div id="outDiv"> <%-- <div> <input type="file" name="upfile"/> <input type="button" value="刪除"/> </div> --%> </div> </td> </tr> <tr> <th></th> <td> <input type="button" value="添加上傳文件" onclick="addLine(this)" /> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="上傳"/> <a href="${pageContext.request.contextPath}/ListFileServlet"> 顯示下載文件 </a> </td> </tr> </table> </form> </body></html>
package cn.itcast.web.servlet.upload;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;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.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;public class UploadServlet2 extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { //取得上傳使用的臨時(shí)和真實(shí)目錄 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload"); //創(chuàng )建上傳文件工廠(chǎng) DiskFileItemFactory factory = new DiskFileItemFactory(); //設置內存中緩存區的大小,默認10K factory.setSizeThreshold(100*1024); //設置上傳文件臨時(shí)存放的目錄 factory.setRepository(new File(tempPath)); //創(chuàng )建上傳文件對象[核心] ServletFileUpload upload = new ServletFileUpload(factory); //設置上傳文件的中文編碼方式 upload.setHeaderEncoding("UTF-8"); //客戶(hù)端上傳文件是否使用MIME協(xié)議, boolean flag = upload.isMultipartContent(request); if(!flag){ //不是以MIME協(xié)議上傳文件 throw new ServletException(); }else{ /*是以MIME協(xié)議上傳的文件,解析request中的所有上傳內容 *每個(gè)內容封裝成一個(gè)對象FileItem, *FileItem代表普通字段和上傳字段二類(lèi) */ try { List<FileItem> fileItemList = upload.parseRequest(request); for(FileItem fileItem : fileItemList){ if(fileItem.isFormField()){ //必定是普通字段 String fieldName = fileItem.getFieldName(); String fieldValue = fileItem.getString("UTF-8"); System.out.println(fieldName+":"+fieldValue); }else { //必定是上傳字段 String realFileName = fileItem.getName(); int index = realFileName.lastIndexOf("\\"); if(index>=0){ //IE6瀏覽器 realFileName = realFileName.substring(index+1); } //通過(guò)真實(shí)文件名換算出唯一個(gè)文件名 String uuidFileName = makeUuidFileName(realFileName); //通過(guò)位運算換算出upload目錄下的子目錄 String uuidFilePath = makeUuidFilePath(uploadPath,uuidFileName); //取得文件輸入流 InputStream is = fileItem.getInputStream(); //取得文件輸出流 OutputStream os = new FileOutputStream(uuidFilePath+"/"+uuidFileName); byte[] buf = new byte[1024]; int len = 0; while( (len=is.read(buf))>0 ){ os.write(buf,0,len); } is.close(); os.close(); //將上傳文件產(chǎn)生的臨時(shí)文件刪除 fileItem.delete(); request.setAttribute("message","上傳文件成功"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); } } } catch (Exception e) { e.printStackTrace(); request.setAttribute("message","上傳文件失敗"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); } } /*方式二 InputStream is = request.getInputStream(); OutputStream os = response.getOutputStream(); byte[] buf = new byte[1024]; int len = 0; while( (len=is.read(buf))>0 ){ os.write(buf,0,len); } is.close(); os.close(); */ /*方式一 String username = request.getParameter("username"); String upfile = request.getParameter("upfile"); System.out.println("上傳用戶(hù):" + username); System.out.println("上傳文件:" + upfile); */ } /* * uploadPath="/upload" * uuidFileName="fdafdsfdsa_a.JPG" * return /upload/8/a 比較難,沒(méi)看懂 */private String makeUuidFilePath(String uploadPath, String uuidFileName) { String uuidFilePath = null ; int code = uuidFileName.hashCode(); // 8 int dir1 = code & 0xF; // 3 int dir2 = code >> 4 & 0xF; // A File file = new File(uploadPath+"/"+dir1+"/"+ dir2); // 如果該目錄未存在 if (! file.exists()){ // 一次性創(chuàng )建N層目錄 file.mkdirs(); } uuidFilePath = file.getPath(); return
uuidFilePath; } /* * realFileName="a.JPG" * return "fdafdsfdsa_a.JPG" */ private String makeUuidFileName(String realFileName) { return UUID.randomUUID().toString()+"_"+realFileName; }}
*2 上傳文件的細節
(1)中文亂碼的問(wèn)題
a)普通字段的中文亂碼問(wèn)題:FileItem.getString("UTF-8"),
注意:FileItem表示上傳表單中的表單項內容
b)上傳字段的中文亂碼問(wèn)題:ServletUploadFile.setHeaderEncoding("UTF-8");
(2)臨時(shí)文件的刪除的問(wèn)題
a)通過(guò)FileItem.delete()
b)一定要在關(guān)閉IO流之后
(3)在同一個(gè)目錄下上傳相同文件名的問(wèn)題
a)將文件名拼接一個(gè)唯一標識符,即UUID
(4)單個(gè)目錄下文件過(guò)多的問(wèn)題
a)采用位運算解決單個(gè)目錄文件過(guò)多
b)思路:參見(jiàn)<<>>
(5)為安全將上傳的文件放入客戶(hù)端無(wú)法直接訪(fǎng)問(wèn)的目錄中的問(wèn)題
a)將上傳的文件,放置到/WEB-INF/upload/目錄下
(6)重構思想
a)做到通用性
package cn.itcast.web.servlet.util;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.List;import java.util.UUID;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import cn.itcast.web.servlet.domain.Up;import cn.itcast.web.servlet.domain.User;import cn.itcast.web.servlet.exception.NoUpfileException;import cn.itcast.web.servlet.exception.UpfileSizeException;public final class UploadUtil { //取得上傳使用的臨時(shí)和真實(shí)目錄 public static final String tempPath = "/WEB-INF/temp"; public static final String uploadPath = "/WEB-INF/upload"; //取得真實(shí)文件名 public static String getRealFileName(String realFileName){ int index = realFileName.lastIndexOf("\\"); if(index>=0){ //IE6瀏覽器 realFileName = realFileName.substring(index+1); } return realFileName; } //取得uuid文件名 public static String makeUuidFilePath(String uploadPath, String uuidFileName) { String uuidFilePath = null; int code = uuidFileName.hashCode();//8 int dir1 = code & 0xF;//3 int dir2 = code >> 4 & 0xF;//A File file = new File(uploadPath+"/"+dir1+"/"+dir2); //如果該目錄未存在 if(!file.exists()){ //一次性創(chuàng )建N層目錄 file.mkdirs(); } uuidFilePath = file.getPath(); return uuidFilePath; } //取得upload/目錄下的分散目錄 public static String makeUuidFileName(String realFileName) { return UUID.randomUUID().toString()+"_"+realFileName; } //文件復制 public static void doSave(InputStream is,String uuidFileName,String uuidFilePath){ OutputStream os = null; try { os = new FileOutputStream(uuidFilePath+"/"+uuidFileName); byte[] buf = new byte[1024]; int len = 0; while( (len=is.read(buf))>0 ){ os.write(buf,0,len); } } catch (Exception e) { e.printStackTrace(); }finally{ if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } //將上傳文件封裝成JavaBean對象中 public static User doUpload(HttpServletRequest request) throws Exception{ User user = new User(); //創(chuàng )建上傳文件工廠(chǎng) DiskFileItemFactory factory = new DiskFileItemFactory(); //設置內存中緩存區的大小,默認10K factory.setSizeThreshold(100*1024); //設置上傳文件臨時(shí)存放的目錄 String tempPath = request.getSession().getServletContext().getRealPath(UploadUtil.tempPath); factory.setRepository(new File(tempPath)); //創(chuàng )建上傳文件對象[核心] ServletFileUpload upload = new ServletFileUpload(factory); //設置上傳文件的中文編碼方式 upload.setHeaderEncoding("UTF-8"); //客戶(hù)端上傳文件是否使用MIME協(xié)議, boolean flag = upload.isMultipartContent(request); if(!flag){ //不是以MIME協(xié)議上傳文件 throw new ServletException(); }else{ /*是以MIME協(xié)議上傳的文件,解析request中的所有上傳內容 *每個(gè)內容封裝成一個(gè)對象FileItem, *FileItem代表普通字段和上傳字段二類(lèi) */ List<FileItem> fileItemList = upload.parseRequest(request); for(FileItem fileItem : fileItemList){ if(fileItem.isFormField()){ //必定是普通字段 String fieldName = fileItem.getFieldName(); String fieldValue = fileItem.getString("UTF-8"); user.setUsername(fieldValue); }else { //必定是上傳字段 //如果無(wú)上傳文件 if(fileItem.getSize()==0){ throw new NoUpfileException(); } String realFileName = UploadUtil.getRealFileName(fileItem.getName()); /*只能上傳JPG文件 if(!realFileName.endsWith("JPG")){ throw new UpfileTypeException(); } */ //只有上傳<=200K的文件 if(fileItem.getSize() > 200 * 1024){ throw new UpfileSizeException(); } //封裝到JavaBean user.getUpfileList().add(fileItem); } }//end of for loop } return user; } public static void doSave(User user, String uploadPath,List<Up> upList) throws Exception { //取得該用戶(hù)上傳的所有文件集合 List<FileItem> fileItemList = user.getUpfileList(); //迭代每個(gè)文件,并上傳 for(FileItem fileItem : fileItemList){ //創(chuàng )建Up對象 Up up = new Up(); up.setUsername(user.getUsername()); //取得輸入流 InputStream is = fileItem.getInputStream(); //取得真實(shí)文件名 String realFileName = fileItem.getName(); realFileName = UploadUtil.getRealFileName(realFileName); //取得UUID文件名 String uuidFileName = UploadUtil.makeUuidFileName(realFileName); //取得UUID文件路徑 String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath,uuidFileName); //保存 UploadUtil.doSave(is,uuidFileName,uuidFilePath); //收集Up信息 up.setUuidFileName(uuidFileName); up.setRealFileName(realFileName); upList.add(up); //刪除臨時(shí)文件 fileItem.delete(); } }}
package cn.itcast.web.servlet.upload;import java.io.IOException;import java.util.ArrayList;import java.util.List;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import cn.itcast.web.servlet.domain.Up;import cn.itcast.web.servlet.domain.User;import cn.itcast.web.servlet.exception.NoUpfileException;import cn.itcast.web.servlet.exception.UpfileSizeException;import cn.itcast.web.servlet.exception.UpfileTypeException;import cn.itcast.web.servlet.service.UpService;import cn.itcast.web.servlet.util.UploadUtil;public class UploadServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { try { User user = UploadUtil.doUpload(request); String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath); List<Up> upList = new ArrayList<Up>(); //寫(xiě)入硬盤(pán) UploadUtil.doSave(user,uploadPath,upList); //寫(xiě)入數據庫表 UpService upService = new UpService(); upService.addUps(upList); request.setAttribute("message","上傳文件成功"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch(UpfileSizeException e){ e.printStackTrace(); request.setAttribute("message","<font color='green'>上傳文件大小限制在200K以?xún)?lt;/font>"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch(UpfileTypeException e){ e.printStackTrace(); request.setAttribute("message","<font color='red'>只能上傳JPG格式的文件</font>"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch(NoUpfileException e){ e.printStackTrace(); request.setAttribute("message","<font color='blue'>無(wú)上傳文件</font>"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch (Exception e) { e.printStackTrace(); request.setAttribute("message","上傳文件失敗"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); } }}
package cn.itcast.web.servlet.domain;import java.util.ArrayList;import java.util.List;import org.apache.commons.fileupload.FileItem;//封裝上傳文件的內容public class User { //上傳用戶(hù) private String username; //上傳的文件 private List<FileItem> upfileList = new ArrayList<FileItem>(); public User(){} public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public List<FileItem> getUpfileList() { return upfileList; } public void setUpfileList(List<FileItem> upfileList) { this.upfileList = upfileList; }}
package cn.itcast.web.servlet.domain;public class Up { private int id; private String username; private String realFileName; private String uuidFileName; public Up(){} public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getRealFileName() { return realFileName; } public void setRealFileName(String realFileName) { this.realFileName = realFileName; } public String getUuidFileName() { return uuidFileName; } public void setUuidFileName(String uuidFileName) { this.uuidFileName = uuidFileName; }}
(7)自定義封裝上傳文件工具類(lèi)的問(wèn)題
課堂練習:無(wú)上傳文件提交/只能上傳JPG或jpg文件
(8)上傳文件的大小的問(wèn)題
(9)上傳多個(gè)文件的問(wèn)題
(10)上傳多個(gè)文件的界面問(wèn)題
Tip:多個(gè)文件上傳的javascript編碼
技巧:
·每次動(dòng)態(tài)增加一個(gè)文件上傳輸入框,都把它和刪除按紐放置在一個(gè)單獨的div中,并對刪除按紐的onclick事件進(jìn)行響應,使之刪除刪除按紐所在的div。
·如:this.parentNode.parentNode.removeChild(this.parentNode);
*3 顯示下載文件列表
a)遞歸方式查詢(xún)可供下載的文件,一定要有出口條件
b)使用Map<UUID文件名,真實(shí)文件名>收集可供下載的文件
c)使用<c:url>和<c:param>對中文名進(jìn)行URL編碼
*4 下載文件
a)對傳過(guò)來(lái)的中文編碼進(jìn)行URL解碼
b)通過(guò)UUID文件名,反向查到該文件所在的真實(shí)目錄
5 文件上傳下載與數據庫結合
a)在將上傳文件保存的同時(shí),寫(xiě)往數據庫表,一個(gè)上傳文件對應一條記錄,確保uuidFileName雙方一致
思考:
a)上傳時(shí),先硬盤(pán),再表?
b)下載時(shí),先硬盤(pán),再表?
c)刪除時(shí),先硬盤(pán),再表?
d)是否需要事務(wù)支持?
Tip:文件下載
Web應用中實(shí)現文件下載的兩種方式
·超鏈接直接指向下載資源
·程序實(shí)現下載需設置兩個(gè)響應頭:
·設置Content-Type 的值為:application/x-msdownload。Web 服務(wù)器需要告訴瀏覽器其所輸出的內容的類(lèi)型不是普通的文本文件或 HTML 文件,而是一個(gè)要保存到本地的下載文件。
·Web 服務(wù)器希望瀏覽器不直接處理相應的實(shí)體內容,而是由用戶(hù)選擇將相應的實(shí)體內容保存到一個(gè)文件中,這需要設置 Content-Disposition 報頭。該報頭指定了接收程序處理數據內容的方式,在 HTTP 應用中只有 attachment 是標準方式,attachment 表示要求用戶(hù)干預。在 attachment 后面還可以指定 filename 參數,該參數是服務(wù)器建議瀏覽器將實(shí)體內容保存到文件中的文件名稱(chēng)。在設置 Content-Dispostion 之前一定要指定 Content-Type.
·因為要下載的文件可以是各種類(lèi)型的文件,所以要將文件傳送給客戶(hù)端,其相應內容應該被當做二進(jìn)制來(lái)處理,所以應該調用 方法返回 ServletOutputStream 對象來(lái)向客戶(hù)端寫(xiě)入文件內容。
package cn.itcast.web.servlet.download;import java.io.File;import java.io.IOException;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import cn.itcast.web.servlet.util.UploadUtil;//顯示可供下載的文件列表public class ListFileServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { //定位下載文件的目錄 String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath); //創(chuàng )建Map<UUID文件名,真實(shí)文件名> Map<String,String> map = new HashMap<String,String>(); //取得下載文件的相關(guān)信息 getFiles(uploadPath,map); //轉發(fā)到list.jsp顯示可供下載的文件 request.setAttribute("map",map); request.getRequestDispatcher("/WEB-INF/list.jsp").forward(request,response); } //遞歸詢(xún)查所有可供下載的文件 private void getFiles(String uploadPath , Map<String,String> map){ File file = new File(uploadPath); //如果file表示文件 if(file.isFile()){//出口 //取得文件名,即UUID文件名 String uuidFileName = file.getName(); int index = uuidFileName.indexOf("_"); String realFileName = uuidFileName.substring(index+1); //存放到Map集合中 map.put(uuidFileName,realFileName); }else{ //必定是目錄 //取得該目錄下的所有內容 File[] files = file.listFiles(); for(File f : files){ //遞歸調用自已 getFiles(f.getPath(),map); } } }}
<%@ page language="java" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <body> <table border="1" align="center"> <caption>下載文件列表</caption> <tr> <th>文件名</th> <th>操作</th> </tr> <c:forEach var="entry" items="${requestScope.map}"> <tr> <td>${entry.value}</td> <td> <c:url var="myURL" value="/DownloadServlet"> <c:param name="uuidFileName" value="${entry.key}"/> </c:url> <a href="${myURL}" style="text-decoration:none"> 下載 </a> </td> </tr> </c:forEach> </table> </body></html>
package cn.itcast.web.servlet.download;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.URLEncoder;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import cn.itcast.web.servlet.util.UploadUtil;//下載文件到本地public class DownloadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { String uuidFileName = request.getParameter("uuidFileName"); byte[] buf = uuidFileName.getBytes("ISO8859-1"); uuidFileName = new String(buf,"UTF-8"); int index = uuidFileName.lastIndexOf("_"); String realFileName = uuidFileName.substring(index+1); response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(realFileName,"UTF-8")); String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath); String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath,uuidFileName); InputStream is = new FileInputStream(uuidFilePath+"/"+uuidFileName); //模式:/WEB-INF/upload/12/4/43213_cc.jpg OutputStream os = response.getOutputStream(); buf = new byte[1024]; int len = 0; while((len=is.read(buf))>0){ os.write(buf,0,len); } is.close(); os.close(); }}
*1 文件上傳下載和數據庫結合
1)速度:較小文件的文件存入數據庫中,取出速度較快,返之較慢。
較大文件的存入硬盤(pán)中,取出速度相對于數據庫較快。
2)同步:
數據庫表和硬盤(pán)必須一致,必須要事務(wù)的支持
在事務(wù)的情況下,表操作優(yōu)先,硬盤(pán)其后
package cn.itcast.web.servlet.domain;public class Up { private int id; private String username; private String realFileName; private String uuidFileName; public Up(){} public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getRealFileName() { return realFileName; } public void setRealFileName(String realFileName) { this.realFileName = realFileName; } public String getUuidFileName() { return uuidFileName; } public void setUuidFileName(String uuidFileName) { this.uuidFileName = uuidFileName; }}
package cn.itcast.web.servlet.dao;import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import cn.itcast.web.servlet.domain.Up;import cn.itcast.web.servlet.util.JdbcUtil;public class UpDao { //增加Up對象 public void addUp(Up up) throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtil.getDataSource()); String sql = "insert into up(username,realFileName,uuidFileName) values(?,?,?)"; runner.update(sql,new Object[]{up.getUsername(),up.getRealFileName(),up.getUuidFileName()}); }}
<?xml version="1.0" encoding="UTF-8"?><c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mydb3</property> </default-config></c3p0-config>
package cn.itcast.web.servlet.util;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import com.mchange.v2.c3p0.ComboPooledDataSource;//JDBC工具類(lèi):關(guān)閉流和取得連接public final class JdbcUtil { private static ComboPooledDataSource dataSource; static{ dataSource = new ComboPooledDataSource(); } //取得數據源 public static ComboPooledDataSource getDataSource() { return dataSource; } //取得連接 public static Connection getMySqlConnection() throws SQLException{ return dataSource.getConnection(); } //關(guān)閉連接 public static void close(Connection conn) throws SQLException{ if(conn!=null){ conn.close(); } } public static void close(PreparedStatement pstmt) throws SQLException { if(pstmt!=null){ pstmt.close(); } } public static void close(ResultSet rs) throws SQLException { if(rs!=null){ rs.close(); } }}
package cn.itcast.web.servlet.upload;import java.io.IOException;import java.util.ArrayList;import java.util.List;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import cn.itcast.web.servlet.domain.Up;import cn.itcast.web.servlet.domain.User;import cn.itcast.web.servlet.exception.NoUpfileException;import cn.itcast.web.servlet.exception.UpfileSizeException;import cn.itcast.web.servlet.exception.UpfileTypeException;import cn.itcast.web.servlet.service.UpService;import cn.itcast.web.servlet.util.UploadUtil;public class UploadServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { try { User user = UploadUtil.doUpload(request); String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath); List<Up> upList = new ArrayList<Up>(); //寫(xiě)入硬盤(pán) UploadUtil.doSave(user,uploadPath,upList); //寫(xiě)入數據庫表 UpService upService = new UpService(); upService.addUps(upList); request.setAttribute("message","上傳文件成功"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch(UpfileSizeException e){ e.printStackTrace(); request.setAttribute("message","<font color='green'>上傳文件大小限制在200K以?xún)?lt;/font>"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch(UpfileTypeException e){ e.printStackTrace(); request.setAttribute("message","<font color='red'>只能上傳JPG格式的文件</font>"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch(NoUpfileException e){ e.printStackTrace(); request.setAttribute("message","<font color='blue'>無(wú)上傳文件</font>"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); }catch (Exception e) { e.printStackTrace(); request.setAttribute("message","上傳文件失敗"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response); } }}
聯(lián)系客服