[TOC]
檔案上傳技術
- Servlet3.0
- JSPSmartUpload:嵌入到JSP中完成檔案上傳.主要用於Model1年代的.
- FileUpload:Apache的檔案上傳元件.
- Struts2:底層是FileUpload.
檔案上傳的要素
- 表單的提交的方式必須是POST.
- 表單中需要有檔案上傳的表單元素:這個元素這個元素必須有name屬性和值
<input type=”file” name=”upload”>
- 表單的enctype屬性的值必須是
multipart/form-data
.
一、檔案上傳原理分析
沒有設定enctype屬性的時候:只能獲得檔案的名稱,而沒有檔案內容,如下圖
設定enctype屬性為multipart/form-data:獲得到檔名及檔案內容,如下圖 檔案上傳的原理圖二、上傳方式
使用Servlet3.0實現檔案上傳
程式碼如下
<body>
<h1>檔案上傳的頁面</h1>
<!--
* 表單的提交的方式必須是POST.
* 表單中需要有檔案上傳的表單元素:這個元素這個元素必須有name屬性和值:<input type=”file” name=”upload”>
* 表單的enctype屬性的值必須是multipart/form-data.
-->
<form action="${ pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data">
<table border="1" width="600">
<tr>
<td>檔案描述</td>
<td><input type="text" name="filedesc"></td>
</tr>
<tr>
<td>檔案上傳</td>
<td><input type="file" name="upload"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="上傳"></td>
</tr>
</table>
</form>
</body>
複製程式碼
/**
* 檔案上傳的Servlet
*/
@WebServlet("/UploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 接收普通資料:
request.setCharacterEncoding("UTF-8");
String filedesc = request.getParameter("filedesc");
System.out.println("檔案描述"+filedesc);
// 接收檔案:
Part part = request.getPart("upload");
long size = part.getSize();// 獲得檔案大小:
System.out.println("檔案大小:"+size);
String name = part.getName();
System.out.println("檔案表單中的name屬性的名稱"+name);
// 獲得檔名:
String header = part.getHeader("Content-Disposition");
int idx = header.lastIndexOf("filename=\"");
String fileName = header.substring(idx+10, header.length()-1);
//IE瀏覽器不行,加上以下程式碼
//String[] split = fileName.split("//");
//fileName = split[split.length-1];
System.out.println("檔名:"+fileName);
// 獲得檔案內容:
InputStream is = part.getInputStream();
// 獲得upload的路徑:
//專案根路徑下不安全,最好存到web-inf目錄下
String path = this.getServletContext().getRealPath("/WEB-INF/upload");
// 獲得檔案的唯一檔名:
String uuidFileName = UUIDUtils.getUUIDFileName(fileName);
String realPath = path+UploadUtils.getPath(uuidFileName);
File file = new File(realPath);
if(!file.exists()){
file.mkdirs();
}
OutputStream os = new FileOutputStream(realPath+"/"+uuidFileName);
byte[] b = new byte[1024];
int len = 0;
while((len = is.read(b))!=-1){
os.write(b, 0, len);
}
is.close();
os.close();
}
複製程式碼
使用fileUpload實現檔案上傳
/*
檔案的上傳:
* 三個要素:
* 提交的方式是POST:
* 表單中需要有<input type=”file” name=”upload”>
* enctype=”multipart/form-data”設定後request.getParamater()就不能用了。
* 檔案上傳的技術:
* Servlet3.0
* JSPSmartUpload
* FileUpload: 上傳檔案大小有限制
* commons-fileupload-1.2.1.jar
* commons-io-1.4.jar
* Struts2
* 使用FileUpload的時候:
* 獲得磁碟檔案工廠物件:
* 通過工廠獲得核心解析類:
* 解析request物件 , 返回集合,集合中的內容是分割線分成的每個部分.
* 遍歷每個部分:
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// 建立一個Product的物件:
Product product = new Product();
// 建立磁碟檔案項工廠.
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
// 設定快取區的大小: 如果檔案的大小超過了緩衝區的大小,就會產生臨時檔案.
diskFileItemFactory.setSizeThreshold(3 * 1024 * 1024);
// 設定臨時檔案存放的路徑:
// diskFileItemFactory.setRepository(repository);
// 獲得核心解析類:ServletFileUpload
ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
fileUpload.setHeaderEncoding("UTF-8");// 解決中文檔名上傳亂碼.
// fileUpload.setFileSizeMax(fileSizeMax); // 設定單個檔案大小
// fileUpload.setSizeMax(sizeMax); // 設定表單中的所有檔案項的檔案總大小
// 解析request 返回List集合
List<FileItem> list = fileUpload.parseRequest(request);
// 獲得每個部分:
// 將遍歷的值存入到一個Map集合中:
Map<String,String> map = new HashMap<String,String>();
String fileName=null;
for (FileItem fileItem : list) {
// 判斷普通項和檔案上傳項:
if(fileItem.isFormField()){
// 普通項
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8"); // 解決的是普通項的中文亂碼.
System.out.println(name+" "+value);
map.put(name, value);
}else{
// 檔案上傳項
// 獲得檔名:
fileName = fileItem.getName();
System.out.println("檔名:"+fileName);
// 獲得檔案的輸入流:
InputStream is = fileItem.getInputStream();
// 獲得檔案要上傳的路徑:
String path = this.getServletContext().getRealPath("/products/1");
OutputStream os = new FileOutputStream(path+"/"+fileName);
int length = 0;
byte[] b= new byte[1024];
while((length = is.read(b))!=-1){
os.write(b, 0, length);
}
is.close();
os.close();
}
}
// 封裝資料:
BeanUtils.populate(product, map);
product.setPid(UUIDUtils.getUUID());
product.setPdate(new Date());
product.setPflag(0);
product.setPimage("products/1/"+fileName);
Category category = new Category();
category.setCid(map.get("cid"));
product.setCategory(category);
// 存入到資料庫:
ProductService productService = (ProductService) BeanFactory.getBean("productService");
productService.save(product);
// 頁面跳轉:
response.sendRedirect(request.getContextPath()+"/AdminProductServlet?method=findByPage&currPage=1");
} catch (Exception e) {
e.printStackTrace();
}
}
複製程式碼
Struts2檔案上傳
瀏覽器端注意事項
表單提交方式method=post 表單中必須有一個元件 表單中必須設定enctype=”multipart/form-data”
伺服器端
Commons-fileupoad.jar包完成。
Struts2框架使用一個fileupload的interceptor來完成檔案上傳
上傳案例
<form action="${ pageContext.request.contextPath }/upMany" enctype="multipart/form-data" method="post">
<input type="file" name="upload">
<input type="file" name="upload">
<input type="submit" value="上傳">
</form>
複製程式碼
public class UploadActionMany extends ActionSupport{
// extends ActionSupport
private File[] upload;
private String[] uploadContentType;
private String[] uploadFileName;
/*
提供對應的set/get方法
*/
public String upload(){
System.out.println(3);
String path = ServletActionContext.getServletContext().getRealPath("/upload");
try {
for (int i = 0; i < upload.length; i++) {
File file = new File(path,uploadFileName[i]);
FileUtils.copyFile(upload[i], file);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(4);
return null;
}
}
複製程式碼
注意事項
<!-- 開發模式 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 設定檔案上傳的大小限制 -->
<constant name="struts.multipart.maxSize" value="40971520"></constant>
<action name="upMany" class="com.lbb.struts2.action.UploadActionMany" method="upload">
<!-- 檔案上傳出錯後的檢視 -->
<result name="input">/error.jsp</result>
<interceptor-ref name="fileUpload">
<!-- <param name="maximumSize"></param> --><!-- 設定每一個檔案的單獨的上傳大小 -->
<!-- <param name="allowedTypes"></param> --><!-- 檔案的mime型別 -->
<param name="allowedExtensions">txt,jpg,bmp</param><!-- 設定允許的字尾名 -->
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
複製程式碼
input接受錯誤資訊
<s:actionerror/>
<s:fielderror/>
複製程式碼
三、檔案上傳問題
檔案重名
使用UUID生成隨機的唯一檔名
同一目錄下檔案過多
一個目錄下檔案過多,導致開啟都很慢,更別說是讀寫操作. 目錄分離方案
- 按時間分:一個月一個目錄,一個星期一個目錄,一天一個目錄
- 按數量分:一個目錄下存5000個檔案,建立一個新的目錄,再去存放
- 按使用者分:為每個使用者建立一個單獨目錄 存放檔案
- 按目錄分離演算法分 使用唯一檔名.hashCode(); -- 得到一個代表當前這個檔案的int型別值. int型別佔4個位元組32位.可以讓hashCode值&0xf; 得到一個int值,用這個int值作為一級目錄. 讓hashCode右移4位 &0xf ;得到一個int值,用這個int值作為二級目錄.依次類推.
public class UploadUtils {
public static String getPath(String uuidFileName){
// 使用唯一檔名.hashCode();
int code1 = uuidFileName.hashCode();
int d1 = code1 & 0xf; // 獲得到1級目錄.
int code2 = code1 >>> 4;
int d2 = code2 & 0xf; // 獲得到2級目錄.
return "/"+d1+"/"+d2;
}
}
複製程式碼
原理圖如下
四、檔案下載
檔案下載的方式
一種:超連結下載.直接將檔案的路徑寫到超連結的href中.---前提:檔案型別,瀏覽器不支援. 二種:手動編寫程式碼的方式完成檔案的下載. 設定兩個頭和一個流: Content-Type:檔案的MIME的型別. Content-Disposition :以下載的形式開啟檔案. InputStream:檔案的輸入流.
<body>
<h1>檔案下載的列表頁面</h1>
<h3>超連結的下載</h3>
<a href="/day10/download/hello.txt">hello.txt</a><br/>
<a href="/day10/download/cs10001.jpg">cs10001.jpg</a><br/>
<a href="/day10/download/hello.zip">hello.zip</a><br/>
<h3>手動編碼方式下載</h3>
<a href="/day10/DownloadServlet?filename=hello.txt">hello.txt</a><br/>
<a href="/day10/DownloadServlet?filename=cs10001.jpg">cs10001.jpg</a><br/>
<a href="/day10/DownloadServlet?filename=hello.zip">hello.zip</a><br/>
<a href="/day10/DownloadServlet?filename=美女.jpg">美女.jpg</a><br/>
</body>
複製程式碼
IE瀏覽器下載中文檔案的時候採用的URL的編碼.
Firefox瀏覽器下載中文檔案的時候採用的是Base64的編碼.
/**
* 檔案下載的Servlet
*/
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.接收引數
String filename = new String(request.getParameter("filename").getBytes("ISO-8859-1"),"UTF-8");
System.out.println(filename);
// 2.完成檔案下載:
// 2.1設定Content-Type頭
String type = this.getServletContext().getMimeType(filename);
response.setHeader("Content-Type", type);
// 2.3設定檔案的InputStream.
String realPath = this.getServletContext().getRealPath("/download/"+filename);
// 根據瀏覽器的型別處理中文檔案的亂碼問題:
String agent = request.getHeader("User-Agent");
System.out.println(agent);
if(agent.contains("Firefox")){
filename = base64EncodeFileName(filename);
}else{
filename = URLEncoder.encode(filename,"UTF-8");
}
// 2.2設定Content-Disposition頭
response.setHeader("Content-Disposition", "attachment;filename="+filename);
InputStream is = new FileInputStream(realPath);
// 獲得response的輸出流:
OutputStream os = response.getOutputStream();
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b))!= -1){
os.write(b, 0, len);
}
is.close();
}
public static String base64EncodeFileName(String fileName) {
BASE64Encoder base64Encoder = new BASE64Encoder();
try {
return "=?UTF-8?B?"
+ new String(base64Encoder.encode(fileName
.getBytes("UTF-8"))) + "?=";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
複製程式碼