文件上傳概述
將本地的文件通過流寫入到服務器的過程。
- QQ上傳照片
- 招聘網站上傳簡歷
文件上傳技術
- JSPSmartUpload:應用在JSP上的文件上傳和下載的組件。
- FileUpload:應用在java環境上的文件上傳的功能。
- Servlet3.0:提供文件上傳的功能。
- Struts2:提供文件上傳的功能。
文件上傳的要素:
- 表單提交的方式需要時POST
- 表單中需要有文件上傳的組件,表單有< input type=“file” >元素,需要有name屬性和值。
- 表單< enctype=“multipart/form-data” >
文件上傳的原理分析
代碼實現
- 引入jar包
- 編寫頁面
- 編寫文件上傳的Servlet
/**
* 文件上傳的Servlet
*/
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1、創建磁盤文件項的工廠
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//2、創建一個核心的解析類
ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
//3、利用核心類解析request,解析後會得到多個部分,返回一個List集合,List集合裝的是每個部分的內容(FileItem文件項)
List<FileItem> list = fileUpload.parseRequest(request);
//4、遍歷list集合,會得到代表每個部分的文件項的對象,根據文件判斷是否是文件上傳項。
for (FileItem fileItem : list) {
//判斷文件是普通項還是上傳項
if(fileItem.isFormField()) {
//普通項,接收普通的值(表單的enctype變更,所以不能用request.getParameter())
String name = fileItem.getFieldName();
//獲得普通項的值
String value = fileItem.getString("UTF-8");
System.out.println(name+" "+value);
}else {
//文件上傳項
//獲得文件上傳項文件的名稱
String filename = fileItem.getName();
//獲得文件上傳項文件的數據
InputStream is = fileItem.getInputStream();
//獲得文件上傳的路徑:磁盤絕對路徑
String realPath = getServletContext().getRealPath("/upload");
//創建一個輸出流,寫入到設置的路徑中
OutputStream os = new FileOutputStream(realPath+"/"+filename);
//兩個流對接
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b)) != -1) {
os.write(b,0,len);
}
is.close();
os.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
文件上傳的API
- DiskFileItemFactory磁盤文件項工廠
- ServletFileUpload核心解析類
- 構造方法:
- ServletFileUpload();
- ServletFileUpload(FileItemFactory fileItemFactory);
- 方法:
- isMultipartContext():判斷表單的enctype屬性是否正確;
- parseRequest():解析Request對象,返回一個List集合(每個部分的對象FileItem);
- setFileSizeMax():設置單個文件上傳大小;
- setSizeMax():設置文件上傳總大小;
- setProgressLstener():設置監聽文件上傳進度;
- setHeaderEncoding():設置中文文件名的上傳亂碼問題
- 構造方法:
- FileItem文件項
- isFormFiled():判斷表單是普通項還是文件上傳項,如果爲true則代表爲普通項;
- getFileName():獲得普通項名稱;
- getString():獲得普通項的值;
- getName():獲得上傳項的名稱;
- getInputStream():獲得上傳的文件內容;
- getSize():獲得文件上傳的文件大小;
- delete():刪除文件上傳過程中的臨時文件;
JS控制多文件上傳
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script>
function add(){
var div1Element = document.getElementById("div1");
div1Element.innerHTML += "<div><input type='file' name='upload'><input type='button' value='刪除' οnclick='del(this)'></div>";
}
function del(who){
var divv = who.parentNode;
divv.parentNode.removeChild(divv);
}
</script>
</head>
<body>
<h1>多文件上傳</h1>
<form action=" ${pageContext.request.contextPath }/UploadServlet " method="post" enctype="multipart/form-data">
<input type="button" value="添加" onclick="add()">
<input type="submit" value="上傳">
<div id="div1"> </div> <br/>
</form>
</body>
</html>
文件上傳中兼容瀏覽器的問題
IE老版本的瀏覽器會出現文件名獲取錯誤的問題,因爲獲取文件名稱的時候會帶有路徑。edge也有此問題。
解決代碼:
//文件上傳項
//獲得文件上傳項文件的名稱
String filename = fileItem.getName();
//System.out.println("文件名:"+filename);
//找到最後一個斜槓
int idx = filename.lastIndexOf("\\");
if(idx != -1) {
//說明找到了,要截掉路徑
filename = filename.substring(idx+1);
}
文件上傳同一個目錄下文件同名的問題
使用唯一文件名進行解決
- 編寫工具類
package com.itheima.utils;
import java.util.UUID;
/**
* 文件上傳的工具類
* @author wj156
*
*/
public class UploadUtils {
/**
* 傳遞一個文件名,返回一個唯一的文件名。
*/
public static String getUuidFileName(String fileName) {
//javaAPI 中有一個類UUID可以產生隨機的字符串
//獲得文件的擴展名
int index = fileName.lastIndexOf(".");
String extetions = fileName.substring(index);
//隨機生成字符串並返回
return UUID.randomUUID().toString()+extetions;
}
}
- 引入工具類
//得到唯一文件名
String uuidFileName = UploadUtils.getUuidFileName(filename);
文件上傳同一個目錄下文件過多的問題
目錄下文件過多,打開會卡頓,且影響讀寫操作。
解題思路:目錄分離
- 按時間分離:按月、周、天等
- 按用戶分離:按張三、李四
- 按個數分離:一個目錄存放3000個文件
- 按目錄分離算法:按某種特定算法進行分離
- 上傳一個文件,得到唯一文件名,獲取hashcode值(32位int類型的值),讓hashcode的值&0xf,得出值爲一級目錄;讓hashcode右移4位&0xf,得出這個值位二級目錄,以此類推。
工具類中編寫方法:
- 上傳一個文件,得到唯一文件名,獲取hashcode值(32位int類型的值),讓hashcode的值&0xf,得出值爲一級目錄;讓hashcode右移4位&0xf,得出這個值位二級目錄,以此類推。
/**
* 目錄分離算法的實現
*/
public static String getRealPath(String uuidFileName) {
int code1 = uuidFileName.hashCode();
int d1 = code1 & 0xf; //獲得一級目錄
int code2 = code1 >>> 4; //右移4位
int d2 = code2 & 0xf; //獲得二級目錄
// 。。。。
return "/"+d1+"/"+d2;
}
應用:
//進行目錄分離:
String path = UploadUtils.getRealPath(uuidFileName);
String newPath = realPath+path;
File file = new File(newPath);
if(!file.exists()) {
file.mkdir();
}