JavaWeb 13 檔案的上傳和下載
1、檔案的上傳
概念
將本地檔案上傳到遠端伺服器上,檔案是以一個流的形式上傳到伺服器中,這就需要用表單提交
對錶單的要求
⑴ 表單的請求方式必須是POST
⑵ 表單的enctype屬性必須是multipart/form-data
① 當該屬性值為application/x-www-form-urlencoded時,在提交表單之前對錶單中的請求引數進行URL編碼。這是預設值
② 當該屬性值為multipart/form-data,在使用包含檔案上傳控制元件的表單時,必須使用該值。表單中的表單項會以一個多部件的形式傳送到伺服器,一個表單項就是一個多部件
⑶ 表單項input中的type屬性是file
2、Fileupload
Fileupload是Apache提供的專門用於進行檔案上傳和下載的工具
DiskFileItemFactory類
檔案專案工廠類
構造方法
public DiskFileItemFactory() {}
建立一個工廠類的例項
ServletFileUpload類
檔案解析器
構造方法
public ServletFileUpload(FileItemFactory fileItemFactory) {}
使用提供的工廠建立一個解析器例項
parseRequest
public List parseRequest(HttpServletRequest request) {}
處理從表單提交過來的multipart/form-data流。傳入request物件,得到表單提交過來的所有表單項
setFileSizeMax
public void setFileSizeMax(long fileSizeMax) {}
限制上傳的單個檔案的大小
setSizeMax
public void setSizeMax(long sizeMax) {}
限制上傳的多個檔案的大小
FileItem介面
解析請求之後,每個表單項(多部件)就是一個FileItem物件
isFormField
boolean isFormField();
是否是一個文字輸入框(type=”text”)。如果是檔案框(type=”file”),返回false
getFieldName
String getFieldName();
獲取表單項的name屬性值
getString
String getString(String encoding) throws UnsupportedEncodingException;
以指定的編碼解析文字輸入框(type=”text”)的value屬性值。一般傳入”UTF-8”,以防中文亂碼
getName
String getName();
獲取檔案框(type=”file”)上傳的檔案的名字
注意:針對IE5、IE6,呼叫該方法獲取到的是上傳檔案的絕對路徑
處理方式:
String fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
這樣不管有沒有檔案分隔符(/ 或 \),都可以獲取檔名
getSize
long getSize();
獲取檔案框(type=”file”)上傳的檔案的大小
getContentType
String getContentType();
獲取檔案框(type=”file”)上傳的檔案的MIME型別
write
void write(File file) throws Exception;
將指定檔案寫到伺服器的硬碟上。傳入指定伺服器路徑的檔案物件
上傳示例
【index.jsp】
<h1>檔案的上傳</h1>
<h2><font color="red">${requestScope.errorMsg}</font></h2>
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
使用者名稱:<input type="text" name="username" /><br /><br />
檔案1:<input type="file" name="file1" /><br /><br />
檔案2:<input type="file" name="file2" /><br /><br />
檔案3:<input type="file" name="file3" /><br /><br />
<input type="submit" value="上傳" />
</form>
【success.jsp】
<h1>檔案上傳成功!</h1>
<a href="${pageContext.request.contextPath}/index.jsp">返回上傳頁面</a>
【FileUploadServlet】
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
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.FileSizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 建立工廠例項
DiskFileItemFactory factory = new DiskFileItemFactory();
// 建立解析器例項
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 設定單個檔案大小5Kb【單位位元組】
fileUpload.setFileSizeMax(5 * 1024);
// 設定多個檔案大小2Mb【單位位元組】
fileUpload.setSizeMax(2 * 1024 * 1024);
try {
// 解析請求獲取每一個表單項
List<FileItem> fileItems = fileUpload.parseRequest(request);
int size = fileItems.size();
for (int i = 0; i < size; i++) {
FileItem fileItem = fileItems.get(i);
// 獲取表單項的name屬性值
String fieldName = fileItem.getFieldName();
// 是否是一個文字輸入框
boolean formField = fileItem.isFormField();
if (formField) {
// 獲取表單value屬性值,以UTF-8來解碼
String value = fileItem.getString("UTF-8");
// 沒有輸入內容
if ("".equals(value)) {
request.setAttribute("errorMsg", "請輸入內容!");
request.getRequestDispatcher("/index.jsp").forward(request, response);
return;
}
System.out.println("表單項name屬性:" + fieldName);
System.out.println("使用者輸入的內容:" + value);
} else {
System.out.println("表單name屬性值:" + fieldName);
// 獲取檔名
String fileName = fileItem.getName();
// 獲取檔案大小(單位位元組)
long fileSize = fileItem.getSize();
// 獲取檔案的MIME型別
String fileType = fileItem.getContentType();
// 沒有上傳檔案
if (0 == fileSize) {
request.setAttribute("errorMsg", "請上傳檔案!");
request.getRequestDispatcher("/index.jsp").forward(request, response);
return;
}
System.out.println("檔名:" + fileName);
System.out.println("檔案大小:" + fileSize + "位元組");
System.out.println("檔案MIME型別:" + fileType);
String uploadPath = "/upload";
// 上傳到伺服器的路徑的絕對路徑
uploadPath = getServletContext().getRealPath(uploadPath);
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {
uploadFile.mkdirs();
}
// 相容IE5、IE6獲取到的檔名是絕對路徑
fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
// 生成UUID
String uuid = UUID.randomUUID().toString().replace("-", "");
// 唯一的檔名
uploadPath = uploadPath + File.separator + uuid + "_" + fileName;
uploadFile = new File(uploadPath);
// 寫到伺服器硬碟上
fileItem.write(uploadFile);
}
}
// 成功,跳轉到成功頁面
response.sendRedirect(request.getContextPath() + "/success.jsp");
} catch (FileSizeLimitExceededException e) {
request.setAttribute("errorMsg", "單個檔案最大5Kb");
request.getRequestDispatcher("/index.jsp").forward(request, response);
System.out.println("單個檔案錯誤資訊:" + e.getMessage());
} catch (SizeLimitExceededException e) {
request.setAttribute("errorMsg", "多個檔案最大2Mb");
request.getRequestDispatcher("/index.jsp").forward(request, response);
System.out.println("多個檔案錯誤資訊:" + e.getMessage());
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、檔案上傳的注意事項
⑴ 匯入正確的包
org.apache.commons.fileupload
不是
org.apache.tomcat.util.http
⑵ 使用者上傳超過指定的單個檔案大小,會報異常:
FileSizeLimitExceededException
超過多個檔案大小,會報異常:
SizeLimitExceededException
在catch這些異常時,有時會給使用者以響應,通過request域設定錯誤資訊,但是當用戶上傳的檔案超過限制的大小過大時,並不會轉發,這是tomcat的bug
4、檔案的下載
概念
將檔案從遠端伺服器下載到本地
檔案下載,只需將要下載的資源的地址放到超連結中,點選即可下載
但如果瀏覽器支援要下載的格式,將會直接開啟,而且通過超連結下載,不能設定許可權,所以一般將要下載的資源放到WEB-INF目錄下,通過Servlet發請求來下載
具體步驟
⑴ 建立一個輸入流,指向WEB-INF下的資原始檔
Tips:可以通過ServletContext的getRealPath方法獲取檔案的真實路徑
⑵ 設定響應頭資訊
① 設定檔案的MIME型別
public void setContentType(String type);
傳入檔案的MIME型別
各種檔案的MIME型別,可以從tomcat伺服器的web.xml中搜索Default MIME Type Mappings檢視
一些MIME:
doc application/msword
jpg或jpeg image/jpeg
json application/json
mp3 audio/mpeg
Tips:通過呼叫ServletContext的getMimeType方法獲取檔案的MIME型別
public String getMimeType(String file);
傳入檔案的絕對路徑,可以獲取檔案的MIME型別
也可以告訴瀏覽器,這是一個檔案流
response.setContentType(“application/octet-stream;charset=UTF-8”);
② 告訴瀏覽器如何處理下載的資源
response.setHeader(“Content-Disposition”, “attachment; filename=”+fileName);
告訴瀏覽器以附件的形式處理下載的資源
如果不設定,瀏覽器如果支援該資源格式,可能會自己開啟資原始檔
Tips:需要先宣告給瀏覽器響應時顯示的檔案的名字(fileName)
⑶ 建立一個輸出流,將資源輸出到瀏覽器
public ServletOutputStream getOutputStream() throws IOException;
獲取輸出流,用於給瀏覽器輸出二進位制資料。通過response物件來呼叫
⑷ 編寫檔案複製的程式碼
可以宣告byte型別的 陣列(緩衝),int型別的 獲取到的資料的長度,通過while迴圈來複制檔案
也可以通過
commons-io-2.5.jar包裡的IOUtils類的copy方法
public static int copy(final InputStream input, final OutputStream output) throws IOException {}
傳入InputStream和OutputStream,即可以複製檔案
對中文檔名的處理
對於Chrome和IE瀏覽器
呼叫URLEncoder的encode方法
public static String encode(String s, String enc) {}
使用指定的編碼機制將字串轉換為 application/x-www-form-urlencoded 格式
URLEncoder.encode(fileName, “UTF-8”);
這是固定寫法,傳入中文檔名,即可對其進行URL編碼
對於火狐瀏覽器
對於火狐瀏覽器需要對中文檔名進行BASE64編碼
需要呼叫sun.misc.BASE64Encoder的encode方法
Tips:可以通過request.getHeader(“User-Agent”);,獲取瀏覽器資訊,再判斷其中是否包含”Mozilla”和”Firefox”,即可判斷是否是火狐瀏覽器
String fileName = "=?utf-8?b?"+new BASE64Encoder().encode(fileName.getBytes())+"?=";
這是固定寫法。需要事先宣告中文檔名
通用方法
fileName = new String(fileName.getBytes(), “ISO-8859-1”);
注意事項
宣告的檔名需要帶副檔名,否則下載的時候,會以沒有副檔名的檔案的形式下載
下載示例
【jsp頁面的body內容】
<a href="${pageContext.request.contextPath}/download">下載資源!</a>
【FileDownloadServlet】
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Encoder;
public class FileDownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String filePath = "/WEB-INF/files/???.???";
ServletContext servletContext = getServletContext();
// 獲取檔案在伺服器上的真實路徑
filePath = servletContext.getRealPath(filePath);
// 輸入流指向資原始檔
InputStream input = new FileInputStream(filePath);
// 獲取資原始檔的MIME型別
String mimeType = servletContext.getMimeType(filePath);
// 設定響應資原始檔的型別
response.setContentType(mimeType);
// 給瀏覽器響應時,顯示的檔名
String fileName = "???.???";
// 獲取瀏覽器資訊
String UA = request.getHeader("User-Agent");
// 判斷是否是火狐瀏覽器
if (UA.contains("Mozilla") && UA.contains("Firefox")) {
// 如果是火狐瀏覽器,需要對中文檔名進行BASE64編碼
fileName = "=?utf-8?b?" + new BASE64Encoder().encode(fileName.getBytes()) + "?=";
} else {
// 其他瀏覽器可以使用URL編碼
fileName = URLEncoder.encode(fileName, "UTF-8");
}
// 告訴瀏覽器以附件形式處理響應的資原始檔
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
// 獲取輸出流
ServletOutputStream output = response.getOutputStream();
// 複製檔案
IOUtils.copy(input, output);
// 關閉流
output.close();
input.close();
}
}