檔案上傳
實作web開發中的檔案上傳功能,操作步驟:
1.在web頁面中添加上傳輸入項。
2.在servlet中讀取上傳檔案的資料,并儲存到本地硬碟中。
<input type=“file”>标簽用于在web頁面中添加檔案上傳輸入項,設定檔案上傳輸入項時
須注意:
1.必須要設定input輸入項的name屬性,否則浏覽器将不會發送上傳檔案的資料。
2.必須把form的enctype屬值設為“
multipart/form-data
”
2.必須把form的method屬性設定為post方式。
附加知識:
enctype屬性規定在發送表單資料之前如何對其進行編碼。屬性可能的值:
application/x-www-form-urlencoded 在發送前編碼所有字元(預設)。
multipart/form-data 不對字元編碼。在使用包含檔案上傳控件的表單時,必須使用該值。
text/plain 空格轉換為 "+" 加号,但不對特殊字元編碼。
方式1:手動實作檔案上傳
案例中上傳的檔案a.txt
web頁面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>手動執行檔案上傳</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <form action="${pageContext.request.contextPath }/TestServlet" method="post" enctype="multipart/form-data"> 使用者名:<input type="text" name="userName"/><hr/> 檔案:<input type="file" name="file1"/> <input type="submit" value="送出"/> </form> </body></html>
Servlet手動擷取上傳檔案
package com.cn.servlet;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import javax.servlet.ServletException;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * 手動擷取檔案上傳 * @author liuzhiyong * */public class TestServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //擷取表單(POST)送出的資料流 ServletInputStream in = request.getInputStream(); //轉換流 InputStreamReader inStream = new InputStreamReader(in); //緩沖流 BufferedReader reader = new BufferedReader(inStream); //輸出資料 String str = null; while((str=reader.readLine()) != null){ System.out.println(str); } //關閉 reader.close(); inStream.close(); in.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); }}
效果:
由于手動上傳檔案,需要另外去解析,是以使用現成的工具,詳見方式2.
方式2:檔案上傳元件(FileUpload元件,推薦)
檔案上傳功能開發中很常用,Apache組織也提供了檔案上傳元件,FileUpload元件。
FileUpload元件使用步驟:
下載下傳元件,引入jar檔案
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
共2個jar包,
點選打開連結,即可使用其API了。
FileUpload元件API:
|-Interface FileItemFactory 檔案上傳工廠類(把每一個請求表單項封裝為一個個FileItem對象)
|--Class DiskFileItemFactory
|----void setRepository(java.io.File repository) 設定臨時緩存目錄
|-Class ServletFileUpload 檔案上傳核心類對象,可以擷取所有的FileItem對象
|----List parseRequest(javax.servlet.http.HttpServletRequest request) 擷取所有檔案上傳項FileItem
判斷上傳表單是否為multipart/form-data類型(即 判斷目前表單是否為檔案上傳表單 ),如果是傳回true
|----void setFileSizeMax(long fileSizeMax) 設定單個檔案上傳最大值
|----void setSizeMax(long sizeMax) 設定總的檔案最大大小
|----void setHeaderEncoding(java.lang.String encoding) 設定上傳的檔案名的編碼,相當于request.setCharacterEncoding(encoding)
|-Interface FileItem 封裝了普通表單項資料以及檔案上傳表單資料
|----String getFieldName() 擷取上傳表單元素名稱
|----String getString() 擷取上傳元素值
|----String getString(java.lang.String encoding) 擷取上傳元素值,并處理格式
|----String getContentType() 擷取上傳檔案類型【僅上傳檔案表單項有資料】
|----InputStream getInputStream() 擷取post方式送出上來的上傳檔案流【僅檔案上傳表單項有資料】
|----String getName() 擷取上傳檔案名
|----void write(java.io.File file) 寫檔案到指定檔案
|----void delete() 删除臨時檔案
使用FileUpload元件測試:
web頁面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>手動執行檔案上傳</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <form action="${pageContext.request.contextPath }/FileUpLoadServlet" method="post" enctype="multipart/form-data"> 使用者名:<input type="text" name="userName"/><hr/> 檔案:<input type="file" name="file1"/> <input type="submit" value="送出"/> </form> </body></html>
Servlet使用元件擷取資料
package com.cn.servlet;import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.List;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.FileItemFactory;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;/** * 檔案上傳元件使用 * @author liuzhiyong * */public class FileUpLoadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.建立檔案上傳工廠類 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();// fileItemFactory.setRepository(repository);//設定臨時目錄 //2.建立檔案上傳核心類對象,可以擷取所有的FileItem對象 ServletFileUpload upload = new ServletFileUpload(fileItemFactory );// upload.setFileSizeMax(fileSizeMax);//設定單個檔案上傳最大值// upload.setSizeMax(sizeMax);//設定總的檔案最大大小// upload.setHeaderEncoding(encoding);//設定上傳的檔案名的編碼,相當于request.setCharacterEncoding(encoding); //判斷目前表單是否為檔案上傳表單,如果是傳回true if(ServletFileUpload.isMultipartContent(request)){ //3.把請求資料轉換為FileItem對象的集合 try { List<FileItem> list = upload.parseRequest(request);//擷取所有檔案上傳項FileItem //周遊,得到每一個上傳項 for(FileItem item : list){ //判斷是普通表單項,還是檔案上傳表單項 if(item.isFormField()){//普通表單 String fieldName = item.getFieldName();//表單元素(這裡是文本框)名稱 String content = item.getString();//表單元素(這裡是文本框)值// String content = item.getString("utf-8");//表單元素(這裡是文本框)值,并處理編碼 }else{//檔案上傳表單 String fieldName = item.getFieldName();//表單元素名稱 String contentType = item.getContentType();//上傳檔案類型 String name = item.getName();//檔案名 InputStream in = item.getInputStream();//檔案流 String content = item.getString();//檔案内容 InputStreamReader inStream = new InputStreamReader(in); //緩沖流 BufferedReader reader = new BufferedReader(inStream); //輸出資料 String str = null; while((str=reader.readLine()) != null){ System.out.println(str); } item.write(new File("d:/aa檔案.txt"));//寫檔案 item.delete();//删除臨時檔案 //關閉 reader.close(); inStream.close(); in.close(); } } } catch (Exception e) { //測試 e.printStackTrace(); } }else{ System.out.println("目前表單不是檔案上傳表單,不處理!"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); }}
效果:
控制台下直接輸出檔案流内容
FileItem類write()寫檔案到硬碟的效果:
寫出的檔案内容如下:
檔案的上傳下載下傳完整案例
需求:
檔案上傳完整案例
1.設定單個檔案不能超過30M
2.設定總大小不能超過50M
3.上傳目錄:上傳到項目資源目錄下的upload目錄
4.上傳檔案不能覆寫,解決上傳檔案名的同名問題
web開始頁面:index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>檔案上傳與下載下傳</title> </head> <body> <a href="${pageContext.request.contextPath }/upload.jsp">檔案上傳</a> <hr/> <a href="${pageContext.request.contextPath }/FileServlet?method=downList">檔案下載下傳清單</a> </body></html>
檔案上傳頁面:upload.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>手動執行檔案上傳</title> </head> <body> <form action="${pageContext.request.contextPath }/FileServlet?method=upload" method="post" enctype="multipart/form-data"> 使用者名:<input type="text" name="userName"/><hr/> 檔案:<input type="file" name="file1"/> <input type="submit" value="送出"/> </form> <%-- 不送出檔案上傳表單,測試 --%> <a href="${pageContext.request.contextPath }/FileServlet?method=upload">通路FileUploadServlet試試</a> </body></html>
檔案下載下傳清單:downList.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%-- 引入jstl核心标簽庫 --%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>My JSP 'downList.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <table> <tr> <th>序号</th> <th>檔案</th> <th>操作</th> </tr> <c:forEach items="${requestScope.fileNameMap }" var="entry" varStatus="varStatus"> <tr> <td>${varStatus.count }</td> <td>${entry.value }</td> <td> <%-- 方式1 --%> <%--<a href="${pageContext.request.contextPath}/FileServlet?method=download&fileName=${entry.key}">下載下傳</a>--%> <%-- 方式2:在JSP頁面中構造一個URL位址 。。注意context不寫,表示預設目前項目路徑下 --%> <c:url var="url" value="/FileServlet" context="${pageContext.request.contextPath}"> <c:param name="method" value="download"></c:param> <c:param name="fileName" value="${entry.key }"></c:param> </c:url> <%-- 使用上面的位址 --%> <a href="${url }">下載下傳</a> </td> </tr> </c:forEach> </table> </body></html>
Servlet處理
package com.cn.servlet;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.URLEncoder;import java.util.HashMap;import java.util.List;import java.util.Map;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.ProgressListener;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;/** * 處理檔案上傳與下載下傳: * 上傳功能: 1.設定單個檔案不能超過30M 2.設定總檔案大小不能超過50M 3.上傳目錄:上傳到項目資源目錄下的upload目錄 4.上傳檔案不能覆寫,解決上傳檔案名的同名問題 下載下傳功能: 下載下傳檔案 * @author liuzhiyong * */public class FileServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //擷取請求參數,區分不同的操作類型 String method = request.getParameter("method"); if("upload".equals(method)){ upload(request, response); }else if("downList".equals(method)){ downList(request, response); }else if("download".equals(method)){ download(request, response); } } /** * 進入下載下傳劉表 * 思路: * 先擷取upload目錄下所有檔案的檔案名,再儲存,跳轉到downList.jsp清單展示 * @param request * @param response * @throws ServletException * @throws IOException */ private void downList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ //1.初始化map集合Map<包含唯一标記的檔案名, 簡單檔案名> Map<String, String> fileNameMap = new HashMap<String, String>(); //2.擷取上傳目錄,及其下所有檔案的檔案名 String bathPath = this.getServletContext().getRealPath("/upload"); //上傳目錄 File file = new File(bathPath); //擷取上傳目錄下,所有檔案名 String[] list = file.list();//傳回目錄下的檔案或者目錄名,包含隐藏檔案。 //周遊,封裝 if(list!=null && list.length>0){ for(String str : list){ //全名 String fileName = str; //擷取全名字元#後面的短名 String shortName = fileName.substring(fileName.lastIndexOf("#")+1); //将全名和短命封裝到Map集合中 fileNameMap.put(fileName, shortName); } } //3.儲存到request域對象中 request.setAttribute("fileNameMap", fileNameMap); //4.轉發到下載下傳清單downList.jsp頁面 request.getRequestDispatcher("/downList.jsp").forward(request, response); } /** * 處理下載下傳 * @param request * @param response * @throws ServletException * @throws IOException */ private void download(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ //擷取使用者下載下傳的檔案名稱(url位址後面追加的參數fileName的值,此參數是GET方式送出的,是以後面需要處理編碼問題) String fileName = request.getParameter("fileName"); //處理編碼 fileName = new String(fileName.getBytes("iso-8859-1"), "utf-8"); //現擷取檔案上傳的目錄路徑 String basePath = this.getServletContext().getRealPath("/upload"); //檔案對象 File file = new File(basePath, fileName); //擷取一個檔案輸入位元組流對象 FileInputStream in = new FileInputStream(file); //如果檔案名是中文,需要進行url編碼,不然下載下傳後中文不顯示 fileName = URLEncoder.encode(fileName, "utf-8"); /** 程式實作下載下傳需設定兩個響應頭: 設定Content-Type 的值為:application/x-msdownload。Web 伺服器需要告訴浏覽器其所輸出的内容的類型不是普通的文本檔案或 HTML 檔案,而是一個要儲存到本地的下載下傳檔案。 Web 伺服器希望浏覽器不直接處理相應的實體内容,而是由使用者選擇将相應的實體内容儲存到一個檔案中,這需要設定 Content-Disposition 報頭。該報頭指定了接收程式處理資料内容的方式, 在 HTTP 應用中隻有 attachment 是标準方式,attachment 表示要求使用者幹預。在 attachment 後面還可以指定 filename 參數, 該參數是伺服器建議浏覽器将實體内容儲存到檔案中的檔案名稱。在設定 Content-Dispostion 之前一定要指定 Content-Type. */ //設定下載下傳的響應頭// response.setContentType("application/x-msdownload");//但是我發現這裡其實可以不加 response.setHeader("content-disposition", "attachment;fileName=" + fileName); //擷取response位元組流 OutputStream out = response.getOutputStream();//因為要下載下傳的檔案可以是各種類型的檔案,是以要将檔案傳送給用戶端,其相應内容應該被當做二進制來處理,是以應該調用輸出位元組流來向用戶端寫入檔案内容。 //緩沖數組 byte[] buff = new byte[1024]; int len = -1; while((len = in.read(buff)) != -1){ out.write(buff, 0, buff.length); } //關閉資源 out.close(); in.close(); } /** * 處理上傳 * @param request * @param response * @throws ServletException * @throws IOException */ private void upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ try { //1.建立檔案上傳工廠類(把每一個請求表單項封裝為一個個FileItem對象) DiskFileItemFactory factory = new DiskFileItemFactory(); //2.建立檔案上傳核心類對象(可以擷取所有的FileItem對象) ServletFileUpload upload = new ServletFileUpload(factory);// //【需求1:設定單個檔案不能超過30M】// upload.setFileSizeMax(30*1024*1024);//30M// //【需求2:設定總檔案大小不超過50M】// upload.setSizeMax(50*1024*1024);//50M //【需求1:設定單個檔案不能超過200M】 upload.setFileSizeMax(200*1024*1024);//200M //【需求2:設定總檔案大小不超過300M】 upload.setSizeMax(300*1024*1024);//300M upload.setHeaderEncoding("utf-8");//設定上傳的檔案名的編碼,若果沒有設定編碼,當上傳檔案名為中文時,會出現亂碼。 /** * ProgressListener顯示上傳進度 */ ProgressListener progressListener = new ProgressListener(){ @Override public void update(long pBytesRead, long pContentLength, int pItems) { System.out.println("到現在為止, " + pBytesRead/1024 + " KB已上傳,總大小為 " + pContentLength/1024 + "KB"); } }; upload.setProgressListener(progressListener); //判斷:上傳表單是否為multipart/form-data類型 if(ServletFileUpload.isMultipartContent(request)){ //3.把請求資料轉換為FileItem的集合 List<FileItem> list = upload.parseRequest(request); //周遊list for(FileItem item : list){ //判斷普通表單元素,或者檔案元素 if(item.isFormField()){//普通表單元素 //擷取元素名稱 String fieldName = item.getFieldName(); //擷取元素名稱對應的值 String value = item.getString("utf-8"); System.out.println(fieldName + ":" + value); }else{//檔案上傳元素 //擷取上傳的檔案名 String name = item.getName(); /** * 問題:檔案重命名,防止上傳後覆寫 * 解決:給使用者添加一個唯一标記 */ //随機生成一個唯一标記 String uuid = UUID.randomUUID().toString().replace("-", ""); name = uuid + "#" + name; //擷取上傳的目錄路徑 String basePath = this.getServletContext().getRealPath("/upload");// /斜杠代表目前伺服器項目路徑下 //建立檔案對象 File file = new File(basePath, name); //寫檔案 //InputStream in = item.getInputStream(); item.write(file); //in.close();//關閉流 item.delete();//删除臨時檔案 } } }else{ System.out.println("不是檔案上傳表單,不處理!"); } } catch (Exception e) { e.printStackTrace(); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); }}
上傳後伺服器中存儲的檔案
下載下傳後的檔案
通用的檔案表單上傳(封裝成指定對象)
package com.cn.utils;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.cn.entity.Book;
public class FileUploadUtils {
/**
* 上傳檔案表單,并且封裝到指定類型中
* @param request 請求
* @param class1 指定封裝的類型
* @return 傳回指定封裝類型的對象
*/
public static <T> T upFile(HttpServletRequest request, Class<T> class1) {
try {
//建立對象
T t = class1.newInstance();
//檔案上傳工廠類(把每一個請求表單項封裝為一個個FileItem對象)
FileItemFactory fileItemFactory= new DiskFileItemFactory();
//檔案上傳核心類對象,可以擷取所有的FileItem對象
ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);
// 判斷上傳表單是否為multipart/form-data類型(即判斷目前表單是否為檔案上傳表單),如果是傳回true
if(fileUpload.isMultipartContent(request)){
//解析請求,擷取所有檔案上傳項FileItem
List<FileItem> list = fileUpload.parseRequest(request);
//周遊,得到每一個上傳項
for(FileItem fileItem : list){
//判斷是否是普通表單項,還是檔案表單項
if(fileItem.isFormField()){
//普通表單項
String fieldName = fileItem.getFieldName();//擷取表單元素名稱
String fieldValue = fileItem.getString("utf-8");//擷取表單值
//封裝到對象中
BeanUtils.setProperty(t, fieldName, fieldValue);
}else{//檔案表單項
String fieldName = fileItem.getFieldName();//擷取表單元素名稱
//擷取檔案名稱
String fileName = fileItem.getName();
/**
* 建立位元組輸出流
*/
//1.擷取檔案輸出路徑
String uploadPath = request.getServletContext().getRealPath("/upload");//和webapps同級的目錄
File f = new File(uploadPath);
if (!f.exists()) {//建立目錄
f.mkdir();
}
//擷取檔案字尾名(例如 .txt .jpg .png等)
String fileNameExtension = fileName.substring(fileName.lastIndexOf("."));
//重新設定檔案名
String imageName = UUID.randomUUID().toString().replace("-", "") + fileNameExtension;
//封裝到對象中
BeanUtils.setProperty(t, fieldName, imageName);
/**
* 寫出到檔案
*/
fileItem.write(new File(uploadPath, imageName));
//删除臨時檔案
fileItem.delete();
}
}
}else{
System.out.println("目前表單不是檔案上傳表單,不處理!");
}
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}