JavaWeb学习(十二)---如何上传文件(附注释超详细的源码)

JavaWeb学习(十二)—如何上传文件(附注释超详细的源码)

01 源码展示

Servlet源码:

准备工作:需要导入common-iocommon-fileupload组件

package com.hooi.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.UUID;

public class UploadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        /*
            1. 通过ServletFileUpload判断用户的请求是普通表单还是带文件的表单
         */
        if (!ServletFileUpload.isMultipartContent(req)){
            //如果是普通表单,直接返回
            return;
            //如果是带有文件的表单,执行步骤2
        }


        /*
            2. 创建文件的保存路径,建议保存在WEB-INF路径下,比较安全,用户无法直接访问该路径下的文件
         */

        //2.1 创建文件的真实保存路径
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()){
            //如果目录不存在,创建这个目录
            uploadFile.mkdir();
        }
        //2.2 创建临时路径,如果文件超过了预期的大小,将它先存放至临时路径下,完成上传后,删除临时文件。
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpfile = new File(tmpPath);
        if (!tmpfile.exists()){
            //如果目录不存在,创建这个目录
            tmpfile.mkdir();
        }

        //===========================至此,完成路径的创建===========================

        /*
            3.使用DiskFileItemFactory加工临时文件的保存目录:
              如:做处理文件上传路径和限制文件大小的加工
              
              一般来说,可以通过流来获取,
              比如request.getInputStream(),
              但是,采用原生态的流获取文件十分麻烦
              因此,我们使用Apache的文件上传组件common-fileupload来实现,
              这个组件依赖于common-io组件。
              
         */

        //3.1.创建DiskFileItemFactory对象
        DiskFileItemFactory factory = new DiskFileItemFactory();

        //3.2 通过工厂设置缓冲区,上传文件大于缓冲区大小时,将该文件先存至临时文件中
        factory.setSizeThreshold(1024*1024);//缓存区大小为1M
        factory.setRepository(tmpfile);//设置临时文件的保存目录,需要传入临时文件的目录

        //========================至此,完成文件目录的加工=========================

        /*
            4.使用ServletFileUpload处理上传文件
              ServletFileUpload对象负责处理上传的文件,并将文件封装为FileItem对象.
              在使用ServletFileUpload对象解析请求时需要传入DiskFileItemFactory对象。
         */

        //4.1 获取ServletFileUpload对象
        //    (需要传入工厂对象(步骤3中经过处理的文件目录))
        ServletFileUpload fileUpload = new ServletFileUpload(factory);

        //4.2 监听文件的上传进度
        fileUpload.setProgressListener(new ProgressListener() {
            @Override
            //pBytesRead:已经读取到的文件的大小
            //pContentLength:文件大小
            public void update(long pBytesRead, long pContentLength, int pItem) {
                System.out.println("文件总大小"+pContentLength+"已上传"+pBytesRead);
            }
        });

        //4.3 处理乱码问题
        fileUpload.setHeaderEncoding("UTF-8");

        //4.4 设置上传单个文件的最大值,单位bit
        fileUpload.setFileSizeMax(1024*1024*10);

        //4.5 设置上传多个文件的最大值(总共可以上传的文件的大小)
        fileUpload.setSizeMax(1024*1024*100);

        //4.6 解析前端请求,并将其封装为FileItem对象
        /*
            FileItem对象: 使用ServletFileUpload对象的parseRequest(Request)方法
            可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,
            然后以List列表的形式返回。

         */
        try {
            List<FileItem> fileItems = fileUpload.parseRequest(req);
            for (FileItem fileItem : fileItems) {
                 //带文件的表单也存在不是文件的表单项,比如本案例中的username,是text属性
                //isFormField();判断上传的文件是普通的表单还是带文件的表单
                if (fileItem.isFormField()){   
                    //getFieldName方法用于返回表单标签name属性的值。
                    String name = fileItem.getFieldName();
                    //getString方法用于将FileItem对象中保存的数据流内容以一个字符串返回
                    String value = fileItem.getString("UTF-8");
                    System.out.println(name+":"+value);
                } else{
                    //getName方法用于获得文件上传字段中的文件名。
                    String uploadFileName = fileItem.getName();
                    System.out.println(""+uploadFileName);

                    if (uploadFileName.trim().equals("")||uploadFileName==null){
                        continue;//文件名不存在或为空字符串,无法保存
                    }


                    //获得上传的文件名 假设选择上传文件时文件的路径为/xxx/xxx/xxx.jpg
                    String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);

                    //获得文件的后缀名
                     /*
                        获取文件后缀名的应用:
                        如果文件后缀名不是网站所要求的文件类型,可以直接return,
                        然后通知用户文件类型不正确
                    */
                    String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".")+1);

                    System.out.println("上传的文件为:"+fileName+" 文件类型:"+fileExtName);


                    /*
                        为了保证文件路径名唯一使用UUID(唯一识别通用码),还可以使用日期区分
                        UUID.randomUUID(); 随机产生一个唯一识别的通用码
                     */
                    //创建文件的路径名
                    String uuidPathName = UUID.randomUUID().toString();


                    //====================至此,完成文件处理=====================

                    /*
                        5. 为每个文件创建存储目录
                     */

                    //给每个文件创建一个对应的目录,
                    //文件将存储在uploadPath目录下的uuidPathName目录下
                    String realPath = uploadPath+"/"+uuidPathName;
                    File realPathFile = new File(realPath);
                    if (!realPathFile.exists()){
                        realPathFile.mkdir();
                    }

                    //=======================至此,完成文件的保存===================

                    /*
                        6. 传输文件内容
                     */
                    //6.1 获得上传文件的流(读取文件内容)
                    InputStream inputStream = fileItem.getInputStream();

                    //6.2 创建文件输出流(写入文件内容)
                    FileOutputStream outputStream = new FileOutputStream(realPath+"/"+fileName);

                    //6.3 创建一个缓冲区
                    byte[] buffer = new byte[1024*8];

                    //6.4 判断是否读取完毕,len大于0说明还有数据没有传输完毕
                    int len = 0;
                    if ((len=inputStream.read(buffer))>0){
                        outputStream.write(buffer,0,len);
                    }

                    //6.5 关闭流
                    inputStream.close();
                    outputStream.close();

                    //6.6 清除临时文件
                    fileItem.delete();
                    
                    //=======================至此,完成文件的传输===================
                    
                    /*
                    	7. 返回上传结果
                    */
                    req.setAttribute("msg","上传成功!");
                    req.getRequestDispatcher("info.jsp").forward(req,resp);


                }
            }

        } catch (FileUploadException e) {
            e.printStackTrace();
            req.setAttribute("msg","上传失败!");
            req.getRequestDispatcher("info.jsp").forward(req,resp);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            req.setAttribute("msg","上传失败!");
            req.getRequestDispatcher("info.jsp").forward(req,resp);
        } catch (IOException e) {
            e.printStackTrace();
            req.setAttribute("msg","上传失败!");
            req.getRequestDispatcher("info.jsp").forward(req,resp);
        }
    }
}

upload.jsp源码:

<body>
<%--
    上传文件需要注意的事项
        1.method必须为post,get无法获取大数据
        2.enctype="multipart/form-data 表示表单中存在数据上传
--%>
<div>
    <form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
        <p>用户名:<input type="text" name="username"></p>
        <p>选择文件:<input type="file" name="file"></p>
        <p><input type="submit" value="上传文件"></p>
    </form>
</div>

</body>
</html>

info.jsp源码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>提示信息</title>
</head>
<body>
    ${pageContext.request.getAttribute("msg")}
</body>
</html>

web.xml源码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.hooi.servlet.UploadServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload.do</url-pattern>
    </servlet-mapping>
</web-app>

02 结果展示

上传文件页面:
在这里插入图片描述
响应消息:
在这里插入图片描述
控制台打印信息:

在这里插入图片描述


03 错误总结

错误1: 在upload.jsp中,忘记键入表单的提交方式method=post,导致在servlet的处理中,直接进入了非文件表单的识别逻辑(即,步骤1)。

错误2: 在获取文件名的代码中,在lastIndexOf()方法中错误的将传入的参数写为了".+1",导致无法获取文件名。

错误3: 在传输文件的过程中,将传入输出流的参数写错,把file对象当文件路径进行了字符串的拼接,导致找不到文件目录的存在。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值