• 上传一个文件


    功能介绍:

      由两个页面组成,页面的生成是根据请求参数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>
    ");
            writer.append("<form method="POST" action="upload" ")
                  .append("enctype="multipart/form-data">
    "); // 以multipart/form-data的编码格式对表单进行编码
            
            // 隐藏域在页面中对于用户是不可见的,在表单中插入隐藏域的目的在于收集或发送信息,以利于被处理表单的程序所使用
            writer.append("<input type="hidden" name="action" ")
                  .append("value="create"/>
    ");
            
            writer.append("<b>文件</b><br/>
    ");
            writer.append("<input type="file" name="file1"/><br/><br/>
    ");
            writer.append("<input type="submit" value="提交"/>
    ");
            writer.append("</form>
    ");
            
            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> 
    ")
                .append("<html> 
    ")
                .append("    <head> 
    ")
                .append("        <title>文件上传</title> 
    ")
                .append("    </head> 
    ")
                .append("    <body> 
    ");
            
            return writer;
        }
        
        private void writeFooter(PrintWriter writer) {
            writer.append("    </body> 
    ").append("</html> 
    ");
        }
        
        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>
    ");
                writer.append("<a href="upload?action=create">上传一个文件")
                      .append("</a><br/><br/>
    ");
            
            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> 
    ").append("<br/><br/> 
    ");
                    }
            }
            
            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());
        }
    }

    结果如下:

    上传文件:

    文件列表:

    文件下载:

  • 相关阅读:
    构建之法阅读笔记04
    团队项目
    构建之法阅读笔记03
    第6周学习进度
    求最大子数组03
    四则运算4(完结)
    第5周学习进度
    敏捷开发概述
    第4周学习进度
    构建之法阅读笔记01
  • 原文地址:https://www.cnblogs.com/aristole/p/8010559.html
Copyright © 2020-2023  润新知