知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
您當(dāng)前位置>首頁 » 新聞資訊 » 網(wǎng)站建設(shè) >
Java web文件上傳下載
發(fā)表時間:2016-7-27
發(fā)布人:葵宇科技
瀏覽次數(shù):37
[版權(quán)申明:本文系作者原創(chuàng),轉(zhuǎn)載請注明出處]
文章出處:http://blog.csdn.net/sdksdk0/article/details/52048666
作者:朱培 ID:sdksdk0 郵箱: zhupei@tianfang1314.cn
本文主要從javaweb上傳文件到服務(wù)器中,并且在服務(wù)器端進行數(shù)據(jù)文件存儲,主要分享了文件上傳原理、使用第三方開源工具進行上傳以及一些文件上傳時需要注意的地方,文件的優(yōu)化處理,還有簡易分享了從我們剛才上傳進去的文件進行下載。需要掌握基本的開發(fā)流程,底層實現(xiàn)原理等。
一、文件上傳原理
- 提供form表單,method必須是post
- form表單的enctype必須是multipart/form-data
- 提供input type=”file”
Enctype屬性
告知服務(wù)器請求正文的MIME類型。
application/x-www-form-urlencoded(默認):
正文:name=aa&password=123
服務(wù)器獲取數(shù)據(jù):request.getParameter(“name”);
文件上傳原理:
解析請求正文的每部分的內(nèi)容。
基于html form表單上傳的數(shù)據(jù)都是以類似—————————–7da3c8e180752{0x130x10}這樣的分割符來標(biāo)記一塊數(shù)據(jù)的起止。
文件上傳的Content-Type為multipart/form-data; boundary=—-WebKitFormBoundaryhQslmBE7nbTLTJzD,而普通的form表單的Content-Type為application/x-www-form-urlencoded。因此,我們可以利用HttpServletRequest的request.getHeaderNames()方法和request.getHeaders(headName)方法得到請求頭Headers中的Content-Type數(shù)據(jù),然后根據(jù)Content-Type數(shù)據(jù)中是否包含multipart/form-data來區(qū)分請求是否為文件上傳請求。其中boundary為文件數(shù)據(jù)的分隔符,用于區(qū)分上傳多個文件。
二、使用第三方工具實現(xiàn)文件上傳
fileupload組件工作流程:
開發(fā)步驟
導(dǎo)入commons-fileupload.jar、commons-io.jar包。
1、界面
我們就是需要一個form表單,為其添加enctype屬性和post方法:
<form action="${pageContext.request.contextPath}/servlet/UploadServlet2" method="post" enctype="multipart/form-data" >
姓名: <input type="text" name="name" /><br />
照片: <input type="file" name="photo" /><br />
<input type="submit" value="提交" />
</form>
2、邏輯處理
我們在一個servlet中進行處理:
request.setCharacterEncoding("UTF-8");
//判斷用戶的請求內(nèi)容是不是multipart/form-data
boolean isMultipart=ServletFileUpload.isMultipartContent(request);
if(!isMultipart){
throw new RuntimeException("error!");
}
//創(chuàng)建DiskFileItemFactory對象
DiskFileItemFactory factory=new DiskFileItemFactory();
//創(chuàng)建核心解析類ServlertFileUpload
ServletFileUpload sfu=new ServletFileUpload(factory);
//解析請求對象
List<FileItem> items=new ArrayList<FileItem>(0);
try {
items=sfu.parseRequest(request);
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(FileItem item:items){
if(item.isFormField()){
processFormField(item);
}else{
processUploadField(item);
}
}
response.getWriter().write("sucess!");
接下來就是分為兩種情況了,一種是對普通的表單元素進行處理,另一種是對文件類的數(shù)據(jù)進行處理,對于第一種情況的話就比較簡單,我們直接獲取名字就可以了,基本上不用過多的處理。
private void processFormField(FileItem item) {
String fieldName=item.getFieldName();
String fieldValue=item.getString();
System.out.println(fieldValue+"="+fieldName);
}
對于第二種情況,需要我們直接對上傳文件進行一系列的處理了,我們首先需要在服務(wù)器上找一個存放文件的地方,然后截取上傳的文件名、構(gòu)建輸出流、關(guān)閉流等操作。
private void processUploadField(FileItem item) {
try {
InputStream in=item.getInputStream();
String filename=item.getName();
//在服務(wù)器上找一個存放文件的地方
String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");
File storeDirectory=new File(storeDirectoryRealPath);
if(!storeDirectory.exists()){
storeDirectory.mkdirs();
}
//截取上傳的文件名
//filename=filename.substring(filename.lastIndexOf(File.separator)+1);
if(filename!=null){
filename=FilenameUtils.getName(filename);
}
String guidFilename=GUIDUtil.generateGUID()+"_"+filename;
//按日期來區(qū)分存儲目錄
// String childDirectory=makeChileDirectory(storeDirectory);
String childDirectory=makeChildDirectory(storeDirectory,guidFilename);
//構(gòu)建輸出流
OutputStream out=new FileOutputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename));
int len = -1;
byte buf[] = new byte[1024];
while((len=in.read(buf))!=-1){
out.write(buf, 0, len);
}
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
三、文件上傳優(yōu)化處理
1、把保存的文件放在用戶無法直接訪問到的地方:例如放在:在WEB-INF/files目錄中。
String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");
2、讓文件名唯一。
String guidFilename=GUIDUtil.generateGUID()+"_"+filename;
//構(gòu)建輸出流
OutputStream out=new FileOutputStream(new File(storeDirectory,guidFilename));
3、避免同一個文件夾中的文件過多。
3.1按照日期進行存儲。
String childDirectory=makeChileDirectory(storeDirectory);
private String makeChileDirectory(File storeDirectory) {
Date now=new Date();
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
String sdate=df.format(now);
File f=new File(storeDirectory,sdate);
if(!f.exists()){
f.mkdirs();
}
return sdate;
}
3.2用文件名的hashCode計算需要進行存儲的目錄,二級目錄。
private String makeChildDirectory(File storeDirectory, String guidFilename) {
int hashCode = guidFilename.hashCode();
int dir1 = hashCode&0xf;// 0~15
int dir2 = (hashCode&0xf0)>>4;//0~15
String s = dir1+File.separator+dir2;
File f = new File(storeDirectory,s);
if(!f.exists()){
f.mkdirs();
}
return s;
}
4、限制文件的大小。web方式不適合上傳大的文件。
4.1單個文件大小:
ServletFileUpload sfu=new ServletFileUpload(factory);
sfu.setFileSizeMax(4*1024*1024);//限制不超過4M
4.2總文件大小:多文件上傳
ServletFileUpload sfu=new ServletFileUpload(factory);
sfu.setSizeMax(8*1024*1024);//總文件大小
5、限制文件的上傳類型。
5.1通過文件擴展名來進行限制。
String extensionName=FilenameUtils.getExtension(filename);
5.2通過文件MIME類型來限制。
String mimeType=item.getContentType();
6、空文件上傳解決方案。
判斷文件名是否為空,當(dāng)文件名為空時return。
7、臨時文件
DiskFileItemFactory的作用是產(chǎn)生FileItem對象。其內(nèi)部有一個緩存,默認大寫拾10kb,如果上傳文件超過10kb,則用磁盤作為緩存。存放緩存的目錄默認是系統(tǒng)的臨時目錄。
DiskFileItemFactory factory=new DiskFileItemFactory();
//更改臨時文件的存放目錄
factory.setRepository(new File("D:/"));
如果是自己用IO流實現(xiàn)的文件上傳,則需要在流關(guān)閉后,清理臨時文件。
FileItem.delete();
可以使用FileItem.write(File f)實現(xiàn)文件上傳的保存。
8、中文編碼
request.setCharacterEncoding("UTF-8");
//該編碼要和jsp頁面保持一致
String fieldValue=item.getString("UTF-8");
9、動態(tài)js控制上傳框
<form action="${pageContext.request.contextPath}/servlet/UploadServlet3" method="post" enctype="multipart/form-data">
name:<input type="text" name="name"/><br/>
<div id="d1">
<div>
photo:<input type="file" name="photo"/><input type="button" value="繼續(xù)上傳" onclick="addFile()"/>
</div>
</div>
<input type="submit" value="上傳"/>
</form>
<script type="text/javascript">
function addFile(){
var d1 = document.getElementById("d1");
var oldInnerHtml = d1.innerHTML;
d1.innerHTML=oldInnerHtml+"<div>photo:<input type='file' name='photo'/><input type='button' value='刪除'>function deleteOne(delBtn){
delBtn.parentNode.parentNode.removeChild(delBtn.parentNode);
}
</script>
四、文件的下載
首先我們來看一下頁面的處理:
新建一個list.jsp文件:
全部資源如下:<br />
<c:forEach items="${map}" var="me">
<c:url value="/servlet/DownLoadServlet" var="url">
<c:param name="filename" value="${me.key}"></c:param>
</c:url>
${me.value} <a href="${url}">下載</a><br/>
</c:forEach>
要記得引入jstl的核心包 。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
接下做界面顯示的servlet。
我們之前上傳的文件是用GUID做過相應(yīng)處理的,是一個拼接好的文件名,用來防止文件同名的情況發(fā)生,這里用戶瀏覽到的文件當(dāng)然要和上傳的時候的文件名 相同,所以這里我們要進行截取,把GUID拼接的前綴去掉,以“_”分開。一個是在服務(wù)器中防同名的文件名以及顯示出來的截取后的文件名。這里以前面說過的,防同名文件方法2:用文件名的hashCode計算需要進行存儲的目錄,二級目錄。這里也就需要使用hashCode找到做過文件的路徑。
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//key:GUID文件名 value:old文件名
Map<String, String> map = new HashMap<String, String>();
//獲取/WEB-INF/files的真實路徑
String rootDirectoryRealPath = getServletContext().getRealPath("/WEB-INF/files");
//遞歸遍歷找出所有的文件
System.out.println(rootDirectoryRealPath);
File rootDirectory = new File(rootDirectoryRealPath);
treeWalk(rootDirectory,map);
//存到請求范圍中,轉(zhuǎn)發(fā)給jsp顯示
request.setAttribute("map", map);
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
//遞歸遍歷找出所有的文件,把文件名高出來
public void treeWalk(File file, Map<String, String> map) {
if(file.isFile()){
String guidFileName = file.getName();
String oldFileName = guidFileName.substring(guidFileName.indexOf("_")+1);
map.put(guidFileName, oldFileName);
}else{
//目錄
File[] childFiles = file.listFiles();
for(File f:childFiles){
treeWalk(f, map);
}
}
}
接下來就是下載的處理了。
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String guidFilename = request.getParameter("filename");//get方式提交的
guidFilename = new String(guidFilename.getBytes("ISO-8859-1"),"UTF-8");
//計算存放路徑
File storeDirectory = new File(getServletContext().getRealPath("/WEB-INF/files"));
String childDirectory = makeChildDirecotry(storeDirectory, guidFilename);// 13/1
//構(gòu)建輸入流
InputStream in = new FileInputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename));
//用響應(yīng)對象的輸出流輸出:下載的方式
String oldFileName = guidFilename.substring(guidFilename.indexOf("_")+1);
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldFileName,"UTF-8"));//不適用火狐
response.setContentType("application/octet-stream");
OutputStream out = response.getOutputStream();
int len = -1;
byte buf[] = new byte[1024];
while((len=in.read(buf))!=-1){
out.write(buf, 0, len);
}
in.close();
}
private String makeChildDirecotry(File storeDirectory, String guidFilename) {
int hashCode = guidFilename.hashCode();
int dir1 = hashCode&0xf;
int dir2 = (hashCode&0xf0)>>4;
String s = dir1+File.separator+dir2;
File f = new File(storeDirectory,s);
if(!f.exists()){
f.mkdirs();
}
return s;
}
通過這個網(wǎng)址進行訪問
http://localhost:8080/UploadDemo/servlet/ShowAllFilesServlet
源碼下載:https://github.com/sdksdk0/UploadDemo
相關(guān)案例查看更多
相關(guān)閱讀
- 跳轉(zhuǎn)小程序
- 云南網(wǎng)站建設(shè)百度
- 云南軟件定制公司
- 網(wǎng)站建設(shè)百度官方
- 昆明軟件定制
- uniapp開發(fā)小程序
- 網(wǎng)站上首頁
- 電商網(wǎng)站建設(shè)
- 云南etc小程序
- 人人商城
- 云南網(wǎng)站建設(shè)靠譜公司
- 云南小程序被騙
- 小程序被騙
- 模版消息
- 小程序商城
- 網(wǎng)站建設(shè)服務(wù)
- 云南網(wǎng)站建設(shè)哪家好
- 昆明做網(wǎng)站
- 迪慶小程序開發(fā)
- 搜索引擎優(yōu)化
- 云南軟件開發(fā)
- 小程序開發(fā)聯(lián)系方式
- 云南旅游網(wǎng)站建設(shè)
- 汽車拆解管理軟件
- 云南小程序開發(fā)課程
- 云南小程序開發(fā)報價
- 服務(wù)器
- 昆明網(wǎng)站開發(fā)
- 全國前十名小程序開發(fā)公司
- 云南小程序開發(fā)制作公司