知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
您當前位置>首頁 » 新聞資訊 » 網(wǎng)站建設(shè) >
JavaWeb之文件上傳與下載詳解
發(fā)表時間:2017-3-15
發(fā)布人:葵宇科技
瀏覽次數(shù):44
文件上傳
文件上傳概述
實現(xiàn)web開發(fā)中的文件上傳功能,需完成如下二步操作:
(1)在web頁面中添加上傳輸入項;
(2)在servlet中讀取上傳文件的數(shù)據(jù),并保存到本地硬盤中。如何在web頁面中添加上傳輸入項?<input type=”file”>標簽用于在web頁面中添加文件上傳輸入項,設(shè)置文件上傳輸入項時須注意:
(1)必須要設(shè)置input輸入項的name屬性,否則瀏覽器將不會發(fā)送上傳文件的數(shù)據(jù);
(2)必須把form的enctype屬值設(shè)為multipart/form-data.設(shè)置該值后,瀏覽器在上傳文件時,將把文件數(shù)據(jù)附帶在http請求消息體中,并使用MIME協(xié)議對上傳的文件進行描述,以方便接收方對上傳數(shù)據(jù)進行解析和處理。如何在Servlet中讀取文件上傳數(shù)據(jù),并保存到本地硬盤中?
(1)Request對象提供了一個getInputStream方法,通過這個方法可以讀取到客戶端提交過來的數(shù)據(jù)。但由于用戶可能會同時上傳多個文件,在servlet端編程直接讀取上傳數(shù)據(jù),并分別解析出相應(yīng)的文件數(shù)據(jù)是一項非常麻煩的工作;
(2)為方便用戶處理文件上傳數(shù)據(jù),Apache 開源組織提供了一個用來處理表單文件上傳的一個開源組件( Commons-fileupload ),該組件性能優(yōu)異,并且其API使用極其簡單,可以讓開發(fā)人員輕松實現(xiàn)web文件上傳功能,因此在web開發(fā)中實現(xiàn)文件上傳功能,通常使用Commons-fileupload組件實現(xiàn)。使用Commons-fileupload組件實現(xiàn)文件上傳,需要導(dǎo)入該組件相應(yīng)的支撐jar包:Commons-fileupload和commons-io。commons-io 不屬于文件上傳組件的開發(fā)jar文件,但Commons-fileupload 組件從1.1 版本開始,它工作時需要commons-io包的支持。
文件上傳原理
對請求正文是multipart/form-data類型的數(shù)據(jù)進行解析。
fileupload工作流程
fileupload核心API
DiskFileItemFactory工廠類
DiskFileItemFactory 是創(chuàng)建 FileItem 對象的工廠,這個工廠類常用方法:
- public DiskFileItemFactory(int sizeThreshold, java.io.File repository):構(gòu)造方法;
- public void setSizeThreshold(int sizeThreshold) :設(shè)置內(nèi)存緩沖區(qū)的大小,默認值為10K。當上傳文件大于緩沖區(qū)大小時, fileupload組件將使用臨時文件緩存上傳文件。
- public void setRepository(java.io.File repository) :指定臨時文件目錄,默認值為當前用戶的系統(tǒng)臨時文件目錄,可通過System.getProperty(“java.io.tmpdir”)打印查看;
ServletFileUpload解析器
ServletFileUpload 負責處理上傳的文件數(shù)據(jù),并將表單中每個輸入項封裝成一個 FileItem 對象中。常用方法有:
- boolean isMultipartContent(HttpServletRequest request) :判斷上傳表單是否為multipart/form-data類型;
- List parseRequest(HttpServletRequest request):解析request對象,并把表單中的每一個輸入項包裝成一個fileItem 對象,并返回一個保存了所有FileItem的list集合;
- setFileSizeMax(long fileSizeMax) :設(shè)置上傳文件的最大值;
- setSizeMax(long sizeMax):設(shè)置上傳文件總量的最大值;
- setHeaderEncoding(java.lang.String encoding) :設(shè)置編碼格式;
文件上傳案例
實現(xiàn)步驟
- 創(chuàng)建DiskFileItemFactory對象,設(shè)置緩沖區(qū)大小和臨時文件目錄;
- 使用DiskFileItemFactory 對象創(chuàng)建ServletFileUpload對象,并設(shè)置上傳文件的大小限制;
- 調(diào)用ServletFileUpload.parseRequest方法解析request對象,得到一個保存了所有上傳內(nèi)容的List對象;
- 對list進行迭代,每迭代一個FileItem對象,調(diào)用其isFormField方法判斷是否是上傳文件;如果為普通表單字段,則調(diào)用getFieldName、getString方法得到字段名和字段值;如果為上傳文件,則調(diào)用getInputStream方法得到數(shù)據(jù)輸入流,從而讀取上傳數(shù)據(jù)。
上傳文件的處理細節(jié)
1、中文文件亂碼問題
文件名中文亂碼問題,可調(diào)用ServletUpLoader的setHeaderEncoding方法,或者設(shè)置request的setCharacterEncoding屬性
2、臨時文件的刪除問題
- 由于文件大小超出DiskFileItemFactory.setSizeThreshold方法設(shè)置的內(nèi)存緩沖區(qū)的大小時,Commons-fileupload組件將使用臨時文件保存上傳數(shù)據(jù),因此在程序結(jié)束時,務(wù)必調(diào)用FileItem.delete方法刪除臨時文件;
- Delete方法的調(diào)用必須位于流關(guān)閉之后,否則會出現(xiàn)文件占用,而導(dǎo)致刪除失敗的情況;
3、文件存放位置
- 為保證服務(wù)器安全,上傳文件應(yīng)保存在應(yīng)用程序的WEB-INF目錄下,或者不受WEB服務(wù)器管理的目錄;
- 為防止多用戶上傳相同文件名的文件,而導(dǎo)致文件覆蓋的情況發(fā)生,文件上傳程序應(yīng)保證上傳文件具有唯一文件名;
- 為防止單個目錄下文件過多,影響文件讀寫速度,處理上傳文件的程序應(yīng)根據(jù)可能的文件上傳總量,選擇合適的目錄結(jié)構(gòu)生成算法,將上傳文件分散存儲;
4、顯示上傳進度
(1)ProgressListener顯示上傳進度
ProgressListener progressListener = new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("到現(xiàn)在為止, " + pBytesRead + " 字節(jié)已上傳,總大小為 "
+ pContentLength);
}
};
upload.setProgressListener(progressListener);
(2)以KB為單位顯示上傳進度
long temp = -1; //temp注意設(shè)置為類變量
long ctemp = pBytesRead /1024;
if (mBytes == ctemp)
return;
temp = mBytes;
編碼實現(xiàn)
使用工具類:GUIDUtils.java、DirectoryUtils.java
package com.study.java.utils;
import java.math.BigInteger;
import java.util.Random;
/**
* @Name: GUIDUtils
* @Description: 創(chuàng)建GUID唯一字符串工具類,參考struts2源碼
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class GUIDUtils {
public static String generateGUID() {
return new BigInteger(165, new Random()).toString().toUpperCase() ;
}
}
package com.study.java.utils;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Name: DirectoryUtils
* @Description: 目錄創(chuàng)建工具類
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class DirectoryUtils {
/**
* @Name: getChildDirectoryByDate
* @Description: 根據(jù)當前日期創(chuàng)建子目錄
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param storeDirectory 文件存儲主目錄
* @Return: String
*/
public static String getChildDirectoryByDate(String storeDirectory) {
Date now = new Date() ;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
String childDirectory = sdf.format(now) ;
File file = new File(storeDirectory, childDirectory) ;
if(!file.exists()) {
file.mkdirs() ;
}
return childDirectory ;
}
/**
* @Name: getChildDirectoryByHashCode
* @Description: 根據(jù)文件名稱字符串的hash碼創(chuàng)建二級子目錄
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param storeDirectory 文件存儲主目錄
* @Parameters: @param filename 文件名稱
* @Return: String
*/
public static String getChildDirectoryByHashCode(String storeDirectory, String filename) {
int hashCode = filename.hashCode() ;
int dir1 = hashCode & 0xf ;
int dir2 = (hashCode & 0xf0) >> 4 ;
String childDirectory = dir1 + "/" + dir2 ;
File file = new File(storeDirectory, childDirectory) ;
if(!file.exists()) {
file.mkdirs() ;
}
return childDirectory ;
}
}
實現(xiàn)文件上傳代碼:UploadServlet.java
package com.study.java.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
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.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.study.java.utils.DirectoryUtils;
import com.study.java.utils.GUIDUtils;
/**
* @Name: UploadServlet
* @Description: 文件上傳
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class UploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8") ;
PrintWriter out = response.getWriter() ;
//設(shè)置請求參數(shù)編碼,適用于post提交,用于處理上傳文件中文編碼問題
request.setCharacterEncoding("UTF-8") ;
//1、創(chuàng)建FileItem工廠對象
DiskFileItemFactory factory = new DiskFileItemFactory() ;
//設(shè)置緩沖區(qū)大小1M,默認10kb
factory.setSizeThreshold(1*1024*1024) ;
//設(shè)置臨時文件存放目錄,默認為當前用戶系統(tǒng)臨時文件存放目錄
String template = getServletContext().getRealPath("/WEB-INF/template") ;
File templatePath = new File(template) ;
if(!templatePath.exists()) {
templatePath.mkdirs() ;
}
factory.setRepository(templatePath) ;
//2、創(chuàng)建上傳文件核心解析器對象
ServletFileUpload sfu = new ServletFileUpload(factory) ;
//判斷表單提交類型是否為multipart/form-data
boolean isMultipartContent = sfu.isMultipartContent(request) ;
if(!isMultipartContent) {
out.write("表單提交類型不正確!!!") ;
}
//限制單文件上傳大小
sfu.setFileSizeMax(3*1024*1024) ;
//限制一次上傳總文件大小
sfu.setSizeMax(10*1024*1024) ;
//3、解析表單提交參數(shù)
try {
List<FileItem> items = sfu.parseRequest(request) ;
//獲取表單提交參數(shù)信息對象
for (FileItem item : items) {
if(item.isFormField()) {
//普通字段
processFormField(item) ;
} else {
//上傳字段
processUploadField(item) ;
}
}
out.write("上傳成功!!!") ;
response.setHeader("Refresh", "2;URL=" + request.getContextPath()) ;
} catch (FileUploadBase.FileSizeLimitExceededException e) {
out.write("單文件上傳大小超出限制3M.") ;
} catch (FileUploadBase.SizeLimitExceededException e) {
out.write("總文件上傳大小超出限制10M.") ;
} catch (FileUploadException e) {
out.write("表單提交參數(shù)解析失敗!!!") ;
}
}
/**
* @Name: processUploadField
* @Description: 上傳表單提交的文件
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param item
* @Return: void
*/
private void processUploadField(FileItem item) {
//獲取文件上傳主目錄
String storeDirectory = getServletContext().getRealPath("/WEB-INF/upload") ;
File file = new File(storeDirectory) ;
if(!file.exists()) {
file.mkdirs() ;
}
//獲取上傳文件名
String filename = item.getName() ;
if(filename != null && !"".equals(filename.toString().trim())) {
filename = GUIDUtils.generateGUID() + "_" + filename.substring(filename.lastIndexOf("//") + 1) ;
}
//獲取文件上傳子目錄
String childDirectory = DirectoryUtils.getChildDirectoryByHashCode(storeDirectory, filename) ;
File f = new File(storeDirectory + "/" + childDirectory, filename) ;
//上傳
try {
item.write(f) ;
} catch (Exception e) {
throw new RuntimeException("文件上傳失敗!!!") ;
}
}
/**
* @Name: processFormField
* @Description: 處理表單提交普通字段,打印到控制臺
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param item
* @Return: void
*/
private void processFormField(FileItem item) {
try {
String fieldName = item.getFieldName() ;
String fieldValue = item.getString("UTF-8") ;
System.out.println(fieldName + "->" + fieldValue);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
文件下載
Web應(yīng)用中實現(xiàn)文件下載的兩種方式
超鏈接直接指向下載資源;
程序?qū)崿F(xiàn)下載:程序?qū)崿F(xiàn)下載需設(shè)置兩個響應(yīng)頭,
(1)設(shè)置Content-Type 的值為:application/x-msdownload。Web 服務(wù)器需要告訴瀏覽器其所輸出的內(nèi)容的類型不是普通的文本文件或 HTML 文件,而是一個要保存到本地的下載文件;
(2)Web 服務(wù)器希望瀏覽器不直接處理相應(yīng)的實體內(nèi)容,而是由用戶選擇將相應(yīng)的實體內(nèi)容保存到一個文件中,這需要設(shè)置 Content-Disposition 報頭。該報頭指定了接收程序處理數(shù)據(jù)內(nèi)容的方式,在 HTTP 應(yīng)用中只有 attachment 是標準方式,attachment 表示要求用戶干預(yù)。在 attachment 后面還可以指定 filename 參數(shù),該參數(shù)是服務(wù)器建議瀏覽器將實體內(nèi)容保存到文件中的文件名稱。在設(shè)置 Content-Dispostion 之前一定要指定 Content-Type。
(3)因為要下載的文件可以是各種類型的文件,所以要將文件傳送給客戶端,其相應(yīng)內(nèi)容應(yīng)該被當做二進制來處理,所以應(yīng)該調(diào)用response.getOutputStream()方法返回 ServeltOutputStream 對象來向客戶端寫入文件內(nèi)容。
response.setContentType("application/x-msdownload") ;
String str = "attachment;filename=" + java.net.URLEncoder.encode(fileName, "UTF-8") ;
response.setHeader("Content-Disposition", str) ;
InputStream is = new FileInputStream(file) ;
OutputStream os = response.getOutputStream() ;
int len = -1 ;
byte[] buf = new byte[1024] ;
while((len = is.read(buf)) != -1) {
os.write(buf, 0, len) ;
os.flush() ;
}
is.close() ;
os.close() ;
下載案例
遍歷上傳目錄下的所有文件顯示給用戶,并允許用戶完成下載。
(1)ShowAllUploadFiles.java
package com.study.java.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
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;
/**
* @Name: ShowAllUploadFiles
* @Description: 顯示所有已經(jīng)上傳的文件
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class ShowAllUploadFiles extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Map<String, String> map = new HashMap<String, String>() ;
//獲取文件上傳主目錄
String storeDirectory = getServletContext().getRealPath("/WEB-INF/upload") ;
File file = new File(storeDirectory) ;
treeWalk(file, map) ;
request.setAttribute("map", map) ;
request.getRequestDispatcher("/listFiles.jsp").forward(request, response) ;
}
/**
* @Name: treeWalk
* @Description: 將文件上傳主目錄中的所有文件名稱放入到Map集合中
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param file
* @Parameters: @param map
* @Return: void
*/
private void treeWalk(File file, Map<String, String> map) {
if(file.isDirectory()) {
File[] list = file.listFiles() ;
for (File f : list) {
treeWalk(f, map) ;
}
} else {
String guidFileName = file.getName() ;
String oldFileName = guidFileName.substring(guidFileName.indexOf("_") + 1) ;
map.put(guidFileName, oldFileName) ;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)JSP視圖頁面
<%@ page language="java" import="java.util.*" 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>
<head>
<title></title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<c:forEach items="${map}" var="me">
<c:url value="/servlet/DownloadServlet" var="url">
<c:param name="guidFileName" value="${me.key}"/>
</c:url><hr/>
${me.value}
<a href="${url}">下載</a><hr/>
</c:forEach>
</body>
</html>
(3)DownloadServlet.java
package com.study.java.servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.study.java.utils.DirectoryUtils;
/**
* @Name: DownloadServlet
* @Description: 文件下載
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//獲取請求鏈接中的參數(shù),并進行編碼,解決文件名中帶中文的情況
String guidFileName = request.getParameter("guidFileName") ;
guidFileName = new String(guidFileName.getBytes("ISO-8859-1"), "utf-8") ;
//獲取文件儲存目錄
String storeDirectory = getServletContext().getRealPath("/WEB-INF/upload") ;
String childDirectory = DirectoryUtils.getChildDirectoryByHashCode(storeDirectory, guidFileName) ;
File file = new File(storeDirectory + "/" + childDirectory, guidFileName) ;
if(file.exists()) {
//通知客戶端下載
String oldFileName = guidFileName.substring(guidFileName.indexOf("_") + 1) ;
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(oldFileName, "UTF-8")) ;
//下載
InputStream is = new FileInputStream(file) ;
OutputStream os = response.getOutputStream() ;
int len = -1 ;
byte[] buf = new byte[1024] ;
while((len = is.read(buf)) != -1) {
os.write(buf, 0, len) ;
os.flush() ;
}
is.close() ;
os.close() ;
} else {
throw new RuntimeException("下載文件不存在!!!") ;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
相關(guān)案例查看更多
相關(guān)閱讀
- 云南花農(nóng)小程序
- 做小程序被騙
- 云南網(wǎng)站建設(shè)服務(wù)公司
- Web開發(fā)框架
- 云南網(wǎng)站設(shè)計
- web服務(wù)
- 報廢車回收管理軟件
- 云南旅游網(wǎng)站建設(shè)
- 云南網(wǎng)站建設(shè)哪家公司好
- 云南網(wǎng)站建設(shè) 網(wǎng)絡(luò)服務(wù)
- 正規(guī)網(wǎng)站建設(shè)公司
- 網(wǎng)站制作哪家好
- 報廢車
- 百度快速排名
- 微信分銷系統(tǒng)
- 模版信息
- 汽車報廢回收
- 小程序定制
- 小程序開發(fā)平臺前十名
- 霸屏推廣
- 網(wǎng)絡(luò)公司報價
- 云南網(wǎng)站建設(shè)特性
- 云南軟件定制
- 快排推廣
- 云南網(wǎng)站建設(shè)案例
- 網(wǎng)站搭建
- 小程序開發(fā)課程
- 網(wǎng)站建設(shè)方案 doc
- 小程序的開發(fā)公司
- 昆明網(wǎng)站制作