上传一个文件

功能介绍:

  由两个页面组成,页面的生成是根据请求参数action的不同,doGet()将生成逻辑委托给相应的方法来完成:

    页面1:显示整个已经上传文件的列表,提供下载支持

    页面2:显示上传文件的页面

配置servlet支持文件上传:

  这里有两种方法:一是使用注解;二是在web.xml中配置

  两种方法的配置如下:

@WebServlet(
        name = "FileUploadServlet",
        urlPatterns = {"/upload"},
        loadOnStartup = 1
)
//告诉web容器为该servlet提供文件上传支持
@MultipartConfig(
        // 告诉web容器文件必须达到5MB时才写入临时目录
        fileSizeThreshold = 5_242_800, // 5MB
        // 上传的文件不能超过20MB
        maxFileSize = 20_971_520L, // 20MB
        // 不能接收超过40MB的请求
        maxRequestSize = 41_942_040L // 40MB
)
<servlet>
      <servlet-name>FileUploadServlet</servlet-name>
      <servlet-class>cn.example.FileUploadServlet</servlet-class>
      <multipart-config>
            <location>/tmp</location><!-- 告诉浏览器在哪里存储临时文件 -->
            <max-file-size>20971520</max-file-size><!--20MB-->
            <max-request-size>41943040</max-request-size><!--40MB-->
            <file-size-threshold>5242880</file-size-threshold>
     </multipart-config>
  </servlet>
  <servlet-mapping>
          <servlet-name>FileUploadServlet</servlet-name>
          <url-pattern>/upload</url-pattern>
  </servlet-mapping>

使用什么来存储上传的文件:

  因为没有学过servlet连接数据库,所以为了方便,直接将上传上来的文件以二进制数组的方式保存在一个Attachment对象中,在servlet中使用一个哈希map作为文件数据库,以文件id(每个文件都有一个唯一的id)为键,以Attachment对象为值。当需要下载某个文件时,需要在请求URL中提供该文件的唯一id,然后在map中返回该文件。因为文件是保存在内存中的,所以这种方法不适宜上传大文件。

 

如何显示页面:

  因为没有学习过jsp,为了使页面具有动态性,所以选择一种比较麻烦的方式,在servlet中直接构建页面。使用HttpServletResponse对象获得一个PrintWriter对象,将html页面代码(包含一些生成的数据)输入到输出流中,就形成了简单的具有动态效果的页面。这项工作主要由doGet()来完成,根据请求参数action的不同,生成不同的页面。

 

如何实现文件的上传:

  1.html表单设置支持文件上传

  2.配置servlet支持文件上传

  3.在servlet中处理上传逻辑:

    3.1获得文件的Part对象,该对象可以表示上传的文件或者表单数据

    3.2从这个Part对象中获得一个输入流,新建一个输出流(ByteArrayOutputStream,它的数据是字节数组的形式写入,因为我需要把上传的文件以字节数组的方法保存在Attachment对象中),把输入流中的数据复制到输出流中,完成之后再把输出流中的内容转换成字节数组保存在Attachment对象中

    3.3生成一个唯一id(使用同步方式,当一个线程正在处理该共享资源时,其他线程需要等待),把它与Attachment对象组成键值对添加到map中

 

如何实现文件的下载:

  1.在请求URL中获得文件id,从map中获取包含该文件的Attachment对象

  2.设置相应头属性Content-Disposition,例如

// 强制浏览器询问用户是保存还是下载文件,而不是在浏览器打开该文件
resp.setHeader("Content-Disposition", "attachment; filename = " + attachment.getName());
// 设置内容类型是通用的、二进制内容类型,这样容器就不会使用字符编码对该数据进行处理
// 更加准确的应该使用附件的MIME内容类型 resp.setContentType("application/octet-stream");

  3.将Attachment对象保存的文件数据输出到输出流中

 

详细代码:

package cn.example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@WebServlet(
        name = "FileUploadServlet",
        urlPatterns = {"/upload"},
        loadOnStartup = 1
)
//告诉web容器为该servlet提供文件上传支持
@MultipartConfig(
        // 告诉web容器文件必须达到5MB时才写入临时目录
        fileSizeThreshold = 5_242_800, // 5MB
        // 上传的文件不能超过20MB
        maxFileSize = 20_971_520L, // 20MB
        // 不能接收超过40MB的请求
        maxRequestSize = 41_942_040L // 40MB
)
public class FileUploadServlet extends HttpServlet{
    private Map<String, Attachment> attachmentDB = new HashMap<String, Attachment>();
    private volatile int TICKET_ID_SEQUENCE = 1;
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String action = req.getParameter("action");
        if(action == null)
            action = "list";
        
        switch (action) {
            case "create":
                    this.showUploadForm(resp);    
                    break;
            case "download":
                    this.downloadAttachment(req, resp);
                    break;
            case "list":
            default:
                    this.listAttachment(resp);
                    break;
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String action = req.getParameter("action");
        if(action == null)
            action = "list";
        
        switch (action) {
            case "create":
                    this.createAttachment(req, resp);    
                    break;
            default:
                this.listAttachment(resp);
                break;
        }
    }

    private void createAttachment(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        Part filePart = req.getPart("file1");
        Attachment attachment = null;
        String idString = null;
        
        if(filePart != null && filePart.getSize() > 0){
            attachment = this.processAttachment(filePart);
            int id;
            synchronized (this) {
                id = this.TICKET_ID_SEQUENCE++;
                idString = Integer.toString(id);
                this.attachmentDB.put(idString, attachment);
            }
        }
        resp.sendRedirect("upload?action=list&id=" + idString);
    }

    private void showUploadForm(HttpServletResponse resp) throws IOException {
        PrintWriter writer = this.writeHeader(resp);
        
        writer.append("<h2>上传一个文件</h2>\r\n");
        writer.append("<form method=\"POST\" action=\"upload\" ")
              .append("enctype=\"multipart/form-data\">\r\n"); // 以multipart/form-data的编码格式对表单进行编码
        
        // 隐藏域在页面中对于用户是不可见的,在表单中插入隐藏域的目的在于收集或发送信息,以利于被处理表单的程序所使用
        writer.append("<input type=\"hidden\" name=\"action\" ")
              .append("value=\"create\"/>\r\n");
        
        writer.append("<b>文件</b><br/>\r\n");
        writer.append("<input type=\"file\" name=\"file1\"/><br/><br/>\r\n");
        writer.append("<input type=\"submit\" value=\"提交\"/>\r\n");
        writer.append("</form>\r\n");
        
        this.writeFooter(writer);
    }
    
    private PrintWriter writeHeader(HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        
        PrintWriter writer = resp.getWriter();
        writer.append("<!DOCTYPE html> \r\n")
            .append("<html> \r\n")
            .append("    <head> \r\n")
            .append("        <title>文件上传</title> \r\n")
            .append("    </head> \r\n")
            .append("    <body> \r\n");
        
        return writer;
    }
    
    private void writeFooter(PrintWriter writer) {
        writer.append("    </body> \r\n").append("</html> \r\n");
    }
    
    private Attachment processAttachment(Part filePart) throws IOException{
        InputStream  inputStream = filePart.getInputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        
        int read;
        final byte[] bytes = new byte[1024];
        
        while((read = inputStream.read(bytes)) != -1){
            outputStream.write(bytes, 0, read);
        }
        
        Attachment attachment = new Attachment();
        attachment.setName(filePart.getSubmittedFileName());
        attachment.setContents(outputStream.toByteArray());
        
        return attachment;
    }
    
    private void listAttachment(HttpServletResponse resp) throws IOException {
        PrintWriter writer = this.writeHeader(resp);
        writer.append("<h2>文件列表</h2>\r\n");
            writer.append("<a href=\"upload?action=create\">上传一个文件")
                  .append("</a><br/><br/>\r\n");
        
        if(attachmentDB.size() == 0){
            writer.append("没有文件可供下载");
        }else{
             for(String id : this.attachmentDB.keySet())
                {
                    Attachment attachment = this.attachmentDB.get(id);
                    writer.append("attachment #").append(id)
                        .append("文件:").append("<a href=\"upload?action=download&id=" + id).append("\">")
                            .append(attachment.getName()).append("</a> \r\n").append("<br/><br/> \r\n");
                }
        }
        
        this.writeFooter(writer);
    }
    
    private void downloadAttachment(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        
        String id = req.getParameter("id");
        if(id == null){
            resp.sendRedirect("upload?action=create");
            return;
        }
        
        Attachment attachment = attachmentDB.get(id);
        if(attachment == null){
            resp.sendRedirect("upload?action=create");
            return;
        }
        
        
        // 强制浏览器询问用户是保存还是下载文件,而不是在浏览器打开该文件
        resp.setHeader("Content-Disposition", "attachment; filename = " + attachment.getName());
        // 设置内容类型是通用的、二进制内容类型,这样容器就不会使用字符编码对该数据进行处理
        // 更加准确的应该使用附件的MIME内容类型
        resp.setContentType("application/octet-stream");
        
        // 使用ServletOutputStream将附件内容输出到响应中
        ServletOutputStream stream = resp.getOutputStream();
        stream.write(attachment.getContents());
    }
}

结果如下:

上传文件:

文件列表:

文件下载:

posted @ 2017-12-09 09:17  Aristole  阅读(343)  评论(0编辑  收藏  举报