Servlet上傳文件和下載文件示例

Servlet上傳文件和下載文件是Java Web應用程序中常見的任務。由於最近我寫了很多關於Java Servlet的文章,所以我想提供一個示例,演示如何將文件上傳到服務器,然後從服務器下載到客戶端。

Servlet上傳文件

我們的用例是提供一個簡單的HTML頁面,客戶端可以在該頁面上選擇本地文件並上傳到服務器。當提交上傳文件的請求時,我們的Servlet程序將把文件上傳到服務器的目錄中,然後提供一個URL,用戶可以通過該URL下載文件。出於安全原因,用戶不會直接提供下載文件的URL,而是會提供一個下載文件的鏈接,我們的Servlet將處理該請求並將文件發送給用戶。我們將在Eclipse中創建一個動態Web項目,項目結構如下圖所示。讓我們來看看我們Web應用程序的所有組件,並了解實現細節。

使用Java上傳文件至伺服器的HTML頁面

我們可以通過向Servlet發送POST請求並提交表單來將文件上傳到伺服器。我們無法使用GET方法上傳文件。另一個需要注意的重點是表單的enctype應該是multipart/form-data。要從用戶文件系統中選擇文件,我們需要使用帶有input元素且typefile的元素。因此,我們可以有一個簡單的HTML頁面index.html用於上傳文件,如下所示:

<html>
<head></head>
<body>
<form action="UploadDownloadFileServlet" method="post" enctype="multipart/form-data">
Select File to Upload:<input type="file" name="fileName">
<br>
<input type="submit" value="Upload">
</form>
</body>
</html>

文件上傳的伺服器文件位置

我們需要將文件存儲到伺服器的某個目錄中,我們可以在程序中將此目錄硬編碼,但為了更好的靈活性,我們將在部署描述符上下文參數中將其配置。同樣,我們將我們的上傳文件HTML頁面添加到歡迎文件列表中。我們的web.xml文件將如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>ServletFileUploadDownloadExample</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <context-param>
    <param-name>tempfile.dir</param-name>
    <param-value>tmpfiles</param-value>
  </context-param>
</web-app>

用於文件上傳位置的ServletContextListener

自從我們需要讀取文件位置的上下文參數並從中創建File對象以便,我們可以在上下文初始化時編寫一個ServletContextListener來執行它。我們可以將絕對目錄位置和File對象設置為上下文屬性,以供其他servlet使用。我們的ServletContextListener實現代碼如下。

package com.journaldev.servlet;

import java.io.File;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class FileLocationContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent servletContextEvent) {
    	String rootPath = System.getProperty("catalina.home");
    	ServletContext ctx = servletContextEvent.getServletContext();
    	String relativePath = ctx.getInitParameter("tempfile.dir");
    	File file = new File(rootPath + File.separator + relativePath);
    	if(!file.exists()) file.mkdirs();
    	System.out.println("File Directory created to be used for storing files");
    	ctx.setAttribute("FILES_DIR_FILE", file);
    	ctx.setAttribute("FILES_DIR", rootPath + File.separator + relativePath);
    }

	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		//如有需要進行清理
	}
	
}

文件上傳下載Servlet

更新: Servlet Specs 3 在 API 中增加了对在服务器上上传文件的支持,因此我们将不需要使用任何第三方 API。请查看Servlet 3 上传文件。对于文件上传,我们将使用Apache Commons FileUpload实用程序,在我们的项目中,我们使用的是1.3版本,FileUpload 依赖于Apache Commons IO jar,因此我们需要将两者都放在项目的 lib 目录中,正如您在上图中看到的项目结构。我们将使用提供解析 HttpServletRequest 对象并返回FileItem列表的DiskFileItemFactory工厂。FileItem 提供了有关需要上传的文件的文件名、表单中字段名、大小和内容类型详细信息的有用方法。要将文件写入目录,我们只需创建一个 File 对象并将其作为参数传递给 FileItem 的write()方法。由于 Servlet 的整个目的是上传文件,我们将覆盖 init() 方法以初始化 Servlet 的DiskFileItemFactory对象实例。我们将在 doPost() 方法实现中使用此对象将文件上传到服务器目录。成功上传文件后,我们将向客户端发送包含文件下载的 URL 的响应,由于 HTML 链接使用 GET 方法,我们将在 URL 中附加文件名的参数,并且我们可以利用相同的 Servlet doGet() 方法来实现文件下载过程。为了实现下载文件 Servlet,首先我们将为文件打开 InputStream,并使用ServletContext.getMimeType()方法获取文件的 MIME 类型,并将其设置为响应的内容类型。我们还需要将响应的内容长度设置为文件的长度。为了确保客户端了解我们正在响应文件,我们需要设置“Content-Disposition”头,其值为“attachment; filename=“fileName”。完成响应配置后,我们可以从 InputStream 读取文件内容并将其写入 ServletOutputStream,然后刷新输出到客户端。我们的最终实现 UploadDownloadFileServlet Servlet 如下。

package com.journaldev.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/UploadDownloadFileServlet")
public class UploadDownloadFileServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    private ServletFileUpload uploader = null;
	@Override
	public void init() throws ServletException{
		DiskFileItemFactory fileFactory = new DiskFileItemFactory();
		File filesDir = (File) getServletContext().getAttribute("FILES_DIR_FILE");
		fileFactory.setRepository(filesDir);
		this.uploader = new ServletFileUpload(fileFactory);
	}
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String fileName = request.getParameter("fileName");
		if(fileName == null || fileName.equals("")){
			throw new ServletException("File Name can't be null or empty");
		}
		File file = new File(request.getServletContext().getAttribute("FILES_DIR")+File.separator+fileName);
		if(!file.exists()){
			throw new ServletException("File doesn't exists on server.");
		}
		System.out.println("File location on server::"+file.getAbsolutePath());
		ServletContext ctx = getServletContext();
		InputStream fis = new FileInputStream(file);
		String mimeType = ctx.getMimeType(file.getAbsolutePath());
		response.setContentType(mimeType != null? mimeType:"application/octet-stream");
		response.setContentLength((int) file.length());
		response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
		
		ServletOutputStream os = response.getOutputStream();
		byte[] bufferData = new byte[1024];
		int read=0;
		while((read = fis.read(bufferData))!= -1){
			os.write(bufferData, 0, read);
		}
		os.flush();
		os.close();
		fis.close();
		System.out.println("File downloaded at client successfully");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		if(!ServletFileUpload.isMultipartContent(request)){
			throw new ServletException("Content type is not multipart/form-data");
		}
		
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.write("<html><head></head><body>");
		try {
			List<FileItem> fileItemsList = uploader.parseRequest(request);
			Iterator<FileItem> fileItemsIterator = fileItemsList.iterator();
			while(fileItemsIterator.hasNext()){
				FileItem fileItem = fileItemsIterator.next();
				System.out.println("FieldName="+fileItem.getFieldName());
				System.out.println("FileName="+fileItem.getName());
				System.out.println("ContentType="+fileItem.getContentType());
				System.out.println("Size in bytes="+fileItem.getSize());
				
				File file = new File(request.getServletContext().getAttribute("FILES_DIR")+File.separator+fileItem.getName());
				System.out.println("Absolute Path at server="+file.getAbsolutePath());
				fileItem.write(file);
				out.write("File "+fileItem.getName()+ " uploaded successfully.");
				out.write("<br>");
				out.write("<a href=\"UploadDownloadFileServlet?fileName="+fileItem.getName()+"\">Download "+fileItem.getName()+"</a>");
			}
		} catch (FileUploadException e) {
			out.write("Exception in uploading file.");
		} catch (Exception e) {
			out.write("Exception in uploading file.");
		}
		out.write("</body></html>");
	}

}

以下圖片顯示了項目的示例執行。

下載 Servlet 檔案上傳下載項目

您可以從以下 URL 下載 Apache Commons IO jar 和 Apache Commons FileUpload jar。 https://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi https://commons.apache.org/proper/commons-io/download_io.cgi

下載 Servlet 檔案上傳示例項目

查看下一篇有關Servlet異常處理的文章。

Source:
https://www.digitalocean.com/community/tutorials/servlet-upload-file-download-example