什么是文件上傳
要將客戶端(瀏覽器)大數(shù)據(jù)存儲(chǔ)到服務(wù)器端,不將數(shù)據(jù)直接存儲(chǔ)到數(shù)據(jù)庫中,
而是要將數(shù)據(jù)存儲(chǔ)到服務(wù)器所在的磁盤上,這要使用文件上傳
為什么使用文件上傳
通過文件上傳,可以將瀏覽器端的大數(shù)據(jù)直接保存到服務(wù)器端金度。
不將數(shù)據(jù)保存到數(shù)據(jù)庫中,而是保存到服務(wù)器磁盤上严沥,這樣減少了數(shù)據(jù)庫服務(wù)器的壓力猜极,
對(duì)數(shù)據(jù)的操作更加靈活
文件上傳原理分析
所謂的文件上傳就是服務(wù)器端通過request對(duì)象獲取輸入流,
將瀏覽器端上傳的數(shù)據(jù)讀取出來消玄,保存到服務(wù)器端跟伏。
上傳文件注意事項(xiàng)
- 1,請(qǐng)求方式必須是post.
- 2,需要使用組件
<input type="file" name="f">
組件必須有名稱. - 3莱找,表單必須設(shè)置encType="multipart/form-data".
服務(wù)器端處理
通過request對(duì)象酬姆,獲取InputStream,可以將瀏覽器提交的所有數(shù)據(jù)讀取到奥溺。
例
<form action="${pageContext.request.contextPath }/servlet/upload3"
method="post" enctype="multipart/form-data">
<p>
用戶名:<input type="text" name="name">
</p>
<p>
文件:<input type="file" name="photo">
</p>
<input type="submit" value="上傳">
</form>
InputStream is = request.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len =is.read(buffer)) !=-1){
System.out.println(new String(buffer,0,len));
}
is.close();
------WebKitFormBoundaryT3WBozNnn4BoAsmx
Content-Disposition: form-data; name="name"
李浩
------WebKitFormBoundaryT3WBozNnn4BoAsmx
Content-Disposition: form-data; name="photo"; filename="aina_cert.sql"
Content-Type: application/octet-stream
------WebKitFormBoundaryT3WBozNnn4BoAsmx--
fileupload 實(shí)現(xiàn)文件上傳,接收用戶上傳的文件
fileupload 是什么
fileupload是由apache的commons組件提供上傳組件骨宠。它最主要的工作就是幫我們解析
request.getnputStram().
導(dǎo)入commons-fileupload 相關(guān) jar包
* commons-fileupload.jar 核心包
* commons-io.jar 依賴包
fileupload 有什么用
form表單encType="multipart/form-data" 上傳文件時(shí)幫助我們 處理表單數(shù)據(jù)浮定。
fileupload 核心類
DiskFileItemFactory ServletFileUpload FileItem
DiskFileItemFactory
- 設(shè)置緩存大小
factory.setSizeThreshold(1024*1024) 設(shè)置為1m默認(rèn)是10k - 設(shè)置臨時(shí)文件存儲(chǔ)位置
File temp = new File(this.getServletContext().getRealPath("temp"));
//可以指定臨時(shí)文件存儲(chǔ)位置,默認(rèn)是系統(tǒng)的臨時(shí)文件存儲(chǔ)位置
factory.setRepository(temp);
ServletFileUpload
parseRequest方法
//得到所有的上傳信息层亿,將每一部分映射成FileItem對(duì)象
List<Fileitme> upload.pareRequest(request)
isMultipartContent 方法
boolean isMultipartContent(request)
這個(gè)方法返回值是boolean 它是用于判斷當(dāng)前表單是否是一個(gè)上傳的表單桦卒,簡單說
就判斷它的encType的值是否是mulipart/form-data。setHeaderEncoding 方法
用于解決上傳文件/表單 中文亂碼問題設(shè)置上傳文件大小
//設(shè)置單個(gè)文件上傳大小
void setFileSizeMax(long fileSizeMax)
//設(shè)置總文件上傳大小
void setSizeMax()
FileItem
boolean isFormField() 判斷當(dāng)前表單字段是否為普通文本字段匿又,如果返回false,說明是文件字段方灾;
String getFieldName() 獲取字段名稱:
<input type="text" name="username">
是指username.String getString("utf-8") 獲取字段的內(nèi)容,如果是文件字段碌更,那么獲取的文件內(nèi)容裕偿,當(dāng)然上傳的文件必須是文本文件;
String getName():獲取文件字段名稱(xxx.png)
String getContextType():獲取上傳的文件mime類型痛单,例如 text/plain.
int getSize() 獲取上傳文件的大小
InputStream getInputStream() 獲取上傳文件對(duì)應(yīng)的輸入流嘿棘。
void write(File):把上傳的文件保存到指定文件中
delete() 刪除臨時(shí)文件
使用步驟
- 1,創(chuàng)建工廠類DiskFileItemFactory 對(duì)象:
DiskFileItemFactory factory = new DiskFileItemFactory()
- 2旭绒,使用工廠創(chuàng)建解析器對(duì)象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
- 3,使用解析器來解析request對(duì)象
List<FileItem> list = fileUpload.parseRequest(request);
例
//判斷表單是否支持文件鸟妙。即enctype="multipart/form-data"
boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
if(!isMultipartContent) {
throw new RuntimeException("your form is not multipart/form-data");
}
//創(chuàng)建工廠類DiskFileItemFactory 對(duì)象
DiskFileItemFactory factory = new DiskFileItemFactory();
//創(chuàng)建一個(gè)ServletFileUpload核心對(duì)象
ServletFileUpload sfu = new ServletFileUpload(factory);
try {
List<FileItem> fileItems = sfu.parseRequest(request);
for(FileItem item:fileItems) {
if(item.isFormField()) { //true 是普通字段 false 文件字段
processFormField(item);
} else{
processUploadField(item);
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 上傳文件
* @param item
*/
private void processUploadField(FileItem fileitem) {
//得到文件輸入流
try {
InputStream is = fileitem.getInputStream();
//項(xiàng)目目錄
String directionRealPath = path+File.separator+"upload";
File storeDirectory = new File(directionRealPath);
if(!storeDirectory.exists()) {
storeDirectory.mkdirs();
}
//文件名稱
File savefile = new File(storeDirectory,fileitem.getName());
fileitem.write(savefile);
fileitem.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
上傳文件時(shí)要考慮的問題
1焦人,允許被瀏覽器端訪問:放置在WebRoot 下,但不能是WEB-INF或META-INF下其子目錄下
2重父,不允許被瀏覽器端訪問: 放置在WEB-INF或META-INF
保證服務(wù)器的安全
把保存上傳文件的目錄放在用戶直接訪問不到的地方(瀏覽器可以訪問到的地方)花椭。
避免文件被覆蓋
讓文件名唯一即可
`filename = UUID.randomUUID() + "_" + filename; `
避免同一個(gè)文件夾中的文件過多
####### 按照日期進(jìn)行打散存儲(chǔ)目錄
private File makeChildDirectory(File storeDirectroy) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateDir = sdf.format(new Date());
File file = new File(storeDirectroy,dateDir);
if(!file.exists()) {
file.mkdirs();
}
return file;
}
####### 用文件名的hashCode計(jì)算打算的存儲(chǔ)目錄:二級(jí)目錄
private File makeChildDirctory(File storeDirectroy,String fileName) {
int hashCode = fileName.hashCode();
//轉(zhuǎn)換成16進(jìn)制
String hex = Integer.toHexString(hashCode);
String childDirctory = hex.charAt(0)+File.separator+hex.charAt(1);
File childDirctoryFile = new File(storeDirectroy,childDirctory);
if(!childDirctoryFile.exists()) {
childDirctoryFile.mkdirs();
}
return childDirctoryFile;
}
####### 完整示例
public class Upload3Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private String path = "/Users/lihao/Downloads/apache-tomcat-8.0.37/webapps/bookmanager/WebContent";
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
throw new RuntimeException("your form is not multipart/form-data");
}
// 設(shè)置文件大小位置等等
DiskFileItemFactory diskfactory = new DiskFileItemFactory();
//指定臨時(shí)文件的存儲(chǔ)目錄
// diskfactory.setRepository(new File("/Users/lihao/"));
// 核心類處理上傳邏輯
ServletFileUpload fileUpload = new ServletFileUpload(diskfactory);
//解決表單亂碼
fileUpload.setHeaderEncoding("utf-8");
//進(jìn)度條
fileUpload.setProgressListener(new ProgressListener() {
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
if (pContentLength == -1) {
System.out.println("文件錯(cuò)誤");
} else {
float percent = (float) (pBytesRead*1.0/pContentLength*100);
int percents = (int) percent;
System.out.println("已完成: "+percents+"%");
}
}
});
try {
// fileUpload.setFileSizeMax(1024*1024*3); //單個(gè)文件不能超過3M
// fileUpload.setSizeMax(1024*1024*6);//總上傳文件不能超過6M
List<FileItem> listItem = fileUpload.parseRequest(request);
for (FileItem item : listItem) {
if (item.isFormField()) {
// 普通文本input
processFormField(item);
} else {
// 文件上傳
if(item.getName() != null && !item.getName().equals("")){
//擴(kuò)展名
System.out.println("擴(kuò)展名:"+FilenameUtils.getExtension(item.getName()));
processFileUpload(item);
} else {
System.out.println("沒有提交文件");
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
//普通input
private void processFormField(FileItem item) {
try {
System.out.println(item.getFieldName() +" "+ item.getString("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// 上傳文件
private void processFileUpload(FileItem item) {
String directory = path + File.separator + "upload";
// upload file name
// f:xxx\sf.png
String filename = item.getName();
if (filename != null) {
filename = FilenameUtils.getName(filename);
}
// 解決重名問題
filename = UUID.randomUUID() + "_" + filename;
File dirctoryFile = new File(directory);
if (!dirctoryFile.exists()) {
dirctoryFile.mkdirs();
}
//按照日期進(jìn)行打算存儲(chǔ)目錄
// File newFileDir = makeChildDirectory(dirctoryFile);
//用文件名的hashCode計(jì)算打散的存儲(chǔ)目錄:二級(jí)目錄
File newFileDir = makeChildDirctory(dirctoryFile,filename);
try {
item.write(new File(newFileDir, filename));
//刪除臨時(shí)文件
item.delete();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 按照日期打散目錄
* @param storeDirectroy
* @return
*/
private File makeChildDirectory(File storeDirectroy) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateDir = sdf.format(new Date());
File file = new File(storeDirectroy,dateDir);
if(!file.exists()) {
file.mkdirs();
}
return file;
}
/**
* 文件名的hashCode 計(jì)算打散的存儲(chǔ)目錄:二級(jí)目錄
* @param storeDirectroy
* @param fileName
* @return
*/
private File makeChildDirctory(File storeDirectroy,String fileName) {
int hashCode = fileName.hashCode();
//轉(zhuǎn)換成16進(jìn)制
String hex = Integer.toHexString(hashCode);
String childDirctory = hex.charAt(0)+File.separator+hex.charAt(1);
File childDirctoryFile = new File(storeDirectroy,childDirctory);
if(!childDirctoryFile.exists()) {
childDirctoryFile.mkdirs();
}
return childDirctoryFile;
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
<form action="${pageContext.request.contextPath }/servlet/upload3"
method="post" enctype="multipart/form-data">
<p>
用戶名:<input type="text" name="name">
</p>
<p>
文件:<input type="file" name="photo">
</p>
<input type="submit" value="上傳">
</form>
2016.11.10