Java实现文件上传详解

文件上传的原理

在这里插入图片描述
你如果要去一个网站,如果发一个请求或文件下载,

电脑为客户端, 服务器为s端,服务器中有网站,web应用,还有文件操作系统File System

IO流,两个池子或许穿东西需要一条管道,两台不同电脑之间的管道就是网络

网络中存在协议,协议就是为了传东西的

传输文件需要响应头和响应体,想要传输文件或者下载文件咱们也要设置固定的响应头和响应体

上传文件首先让浏览器能够支持文件上传 input(file)

input file 通过网络上传到web应用,再从web应用传到文件系统

http 80

https 443

URL:指向互联网上的一个具体的地址

NetWork网络传输如何写?

写网络传输 工程十分浩大,所以我们直接用现有的java类

文件上传的注意事项

  1. 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下
  2. 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名(-时间戳 -uuid -md5 -位运算)
  3. 要限制上传文件的最大值
  4. 可以限制上传文件的类型,在收到上传文件名时,判断后缀是否合法

上传文件需要用到的类

需要用到的类

ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,在使用

ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。所以,我们需要在进行解析工作前构造好

DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象

fileItemFactory属性

ServletFileUpload类

ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中,使用其parseRequest(HttpServletRequest)方法

可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,然后以List列表的形式返回,使用该方法处理上传文件简单易用
trim()方法去掉首尾空格

实现过程

前端提交form表导致FileServlet中
在这里插入图片描述

我们在FileServlet中实现上传文件的功能
1.首先
判断上传的文件是普通表单还是带文件的表单

 if(!ServletFileUpload.isMultipartContent(request)){
            return;//终止方法运行,说明这是一个普通的表单,直接返回
  }

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

 String uploadPath=this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile=new File(uploadPath);
        if(!uploadFile.exists()){
            uploadFile.mkdir();//创建这个目录
        }
        System.out.println("创建目录成功");

3.缓存,临时文件:文件超过规定大小,被弄成临时文件,过几天自动删除,或者提醒用户转存为永久

String tmpPath=this.getServletContext().getRealPath("/WEB-INF/upload");
        File file=new File(tmpPath);
        if(!file.exists()){
            file.mkdir();//创建这个目录
        }

4.处理上传的文件,一般需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件:
🌟创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的:
DiskFileItemFactory factory=new DiskFileItemFactory();
通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中:
factory.setSizeThreshold(1024*1024);
actory.setRepository(file);

DiskFileItemFactory factory=getDiskFileItemFactory(file);
//======================================================
DiskFileItemFactory getDiskFileItemFactory(File file){
        DiskFileItemFactory factory=new DiskFileItemFactory();
        //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中:
        factory.setSizeThreshold(1024*1024);
        factory.setRepository(file);
        return factory;
    }

5.获取ServletFileload 用来监听
监听文件的上传进度

ServletFileUpload upload=getServletFileUpload(factory);
//=====================================================
ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
        ServletFileUpload upload=new ServletFileUpload(factory);
        //🌟监听文件上传的进度
        upload.setProgressListener(new ProgressListener() {
            @Override
            //pBytesRead:已读取到的文件大小
            //pContentLength:文件大小
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
            }
        });
        //处理乱码问题
        upload.setHeaderEncoding("UTF-8");
        //设置单个文件的最大值
        upload.setFileSizeMax(1024*1024*10);
        //设置总共能够上传文件的大小
        //1024=1kb*1024=1M*10=10M
        upload.setSizeMax(1024*1024*10);
        return  upload;
    }

6.处理上传文件

String msg=uploadParseRequest(upload,request,uploadPath);
//=======================================================
String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request,String uploadPath){
        String msg="上传失败";
        try {
            List<FileItem> fileItems=upload.parseRequest(request);
            //fileItem每一个表单对象
            for (FileItem fileItem : fileItems) {
                //判断上传的文件是普通的表单还是带文件的表单
                if(fileItem.isFormField()){
                    //getFiledName指的是前端表单控件的name;
                    String name=fileItem.getFieldName();
                    String value=fileItem.getString("UTF-8");
                    System.out.println(name+":"+value);
                }else{
                    //如果是文件表单
                    //1.处理文件=================
                    String uploadFileName=fileItem.getName();
                    System.out.println("上传的文件名:"+uploadFileName);
                    //可能存在文件名不合法的情况
                    if(uploadFileName.trim().equals("")||uploadFileName==null){
                        continue;
                    }
                    //获得上传的文件名 /images/gurl/paojie.png
                    String fileName=uploadFileName.substring(uploadFileName.lastIndexOf("/")+1);
                    //获得文件的后缀名
                    String fileExtName=uploadFileName.substring(uploadFileName.lastIndexOf(".")+1);
                    /*
                     * 如果文件的后缀名fileExtName不是我们所要的
                     * 就直接return 不处理,告诉用户文件类型不对
                     * */
                    //可以使用UUID(唯一识别的通用码),保证文件名唯一
                    //UUID.randomUUID() 随机生成一个唯一识别的通用码
                    //网络传输中的东西都需要序列化,
                    //POJO,实体类只能在本机上用,如果想要在多个电脑上运行,传输===》需要把对象都序列化了
                    //implements Serializable 没有方法的接口-》标记接口。JVM虚拟机在运行的时候,JVM的本地方法栈-- native->c++
                    //JNI=java Native Interface->java 本地化接口
                    //Java栈
                    //UUID 通过位运算,生成随机序列号保证不重复性
                    String uuidPath = UUID.randomUUID().toString();
                    //2.存放地址=================
                    //存到哪?uploadPath
                    //文件真实存在的路径realPath
                    String realPath=uploadPath+"/"+uuidPath;
                    //给每个文件创建一个对应的文件夹
                    File realPathFile=new File(realPath);
                    if(!realPathFile.exists()){
                        realPathFile.mkdir();
                    }
                    //3.文件传输=================
                    //获取文件上传的流
                    InputStream inputStream=fileItem.getInputStream();
                    //创建一个文件输出流
                    //realPath=真实的文件夹
                    //差了一个文件,加上输出文件的名字+"/"+uuidFileName
                    FileOutputStream fos=new FileOutputStream(realPath+"/"+fileName);
                    //创建一个缓冲区
                    byte[] buffer=new byte[1024*1024];
                    //判断是否读取完毕
                    int len=0;
                    //如果大于0说明还存在数据
                    while((len=inputStream.read(buffer))>0){
                        fos.write(buffer,0,len);
                    }
                    msg="上传文件成功";
                    //关闭流
                    fos.close();
                    inputStream.close();
                    fileItem.delete();//上传文件成功,清除临时文件

                }

            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  msg;
    }

7.最后得到文件上传的结果利用Servlet请求转发

 //servlet请求转发消息
        request.setAttribute("msg",msg);
        request.getRequestDispatcher("info.jsp").forward(request,response);

完整代码

package com.lding.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.http.HttpServletRequest;
import java.io.*;
import java.util.List;
import java.util.UUID;

/**
 * @program: FileTransport
 * @description: ${description}
 * @author: 王丁
 * @date: 2021-11-11 11:25
 **/
public class FileServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        System.out.println("进入FileServlet");
        //判断上传的文件是普通表单还是带文件的表单
        if(!ServletFileUpload.isMultipartContent(request)){
            return;//终止方法运行,说明这是一个普通的表单,直接返回
        }
        //创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
        String uploadPath=this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile=new File(uploadPath);
        if(!uploadFile.exists()){
            uploadFile.mkdir();//创建这个目录
        }
        System.out.println("创建目录成功");
        //缓存,临时文件
        //文件超过规定大小,被弄成临时文件,过几天自动删除,或者提醒用户转存为永久
        String tmpPath=this.getServletContext().getRealPath("/WEB-INF/upload");
        File file=new File(tmpPath);
        if(!file.exists()){
            file.mkdir();//创建这个目录
        }
        //处理上传的文件,一般需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
        //但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件:
        /*
            ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileTtem对象
            在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
            所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象
            通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象
            对象的fileItemFactory属性
         */

        DiskFileItemFactory factory=getDiskFileItemFactory(file);
        //        //🌟监听文件上传的进度
        ServletFileUpload upload=getServletFileUpload(factory);
        //3.🌟处理上传文件
        String msg=uploadParseRequest(upload,request,uploadPath);
   

        //servlet请求转发消息
        request.setAttribute("msg",msg);
        request.getRequestDispatcher("info.jsp").forward(request,response);


    }
    DiskFileItemFactory getDiskFileItemFactory(File file){
        DiskFileItemFactory factory=new DiskFileItemFactory();
        //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中:
        factory.setSizeThreshold(1024*1024);
        factory.setRepository(file);
        return factory;
    }
    ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
        ServletFileUpload upload=new ServletFileUpload(factory);
        //🌟监听文件上传的进度
        upload.setProgressListener(new ProgressListener() {
            @Override
            //pBytesRead:已读取到的文件大小
            //pContentLength:文件大小
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
            }
        });
        //处理乱码问题
        upload.setHeaderEncoding("UTF-8");
        //设置单个文件的最大值
        upload.setFileSizeMax(1024*1024*10);
        //设置总共能够上传文件的大小
        //1024=1kb*1024=1M*10=10M
        upload.setSizeMax(1024*1024*10);
        return  upload;
    }
    String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request,String uploadPath){
        String msg="上传失败";
        try {
            List<FileItem> fileItems=upload.parseRequest(request);
            //fileItem每一个表单对象
            for (FileItem fileItem : fileItems) {
                //判断上传的文件是普通的表单还是带文件的表单
                if(fileItem.isFormField()){
                    //getFiledName指的是前端表单控件的name;
                    String name=fileItem.getFieldName();
                    String value=fileItem.getString("UTF-8");
                    System.out.println(name+":"+value);
                }else{
                    //如果是文件表单
                    //1.处理文件=================
                    String uploadFileName=fileItem.getName();
                    System.out.println("上传的文件名:"+uploadFileName);
                    //可能存在文件名不合法的情况
                    if(uploadFileName.trim().equals("")||uploadFileName==null){
                        continue;
                    }
                    //获得上传的文件名 /images/gurl/paojie.png
                    String fileName=uploadFileName.substring(uploadFileName.lastIndexOf("/")+1);
                    //获得文件的后缀名
                    String fileExtName=uploadFileName.substring(uploadFileName.lastIndexOf(".")+1);
                    /*
                     * 如果文件的后缀名fileExtName不是我们所要的
                     * 就直接return 不处理,告诉用户文件类型不对
                     * */
                    //可以使用UUID(唯一识别的通用码),保证文件名唯一
                    //UUID.randomUUID() 随机生成一个唯一识别的通用码
                    //网络传输中的东西都需要序列化,
                    //POJO,实体类只能在本机上用,如果想要在多个电脑上运行,传输===》需要把对象都序列化了
                    //implements Serializable 没有方法的接口-》标记接口。JVM虚拟机在运行的时候,JVM的本地方法栈-- native->c++
                    //JNI=java Native Interface->java 本地化接口
                    //Java栈
                    //UUID 通过位运算,生成随机序列号保证不重复性
                    String uuidPath = UUID.randomUUID().toString();
                    //2.存放地址=================
                    //存到哪?uploadPath
                    //文件真实存在的路径realPath
                    String realPath=uploadPath+"/"+uuidPath;
                    //给每个文件创建一个对应的文件夹
                    File realPathFile=new File(realPath);
                    if(!realPathFile.exists()){
                        realPathFile.mkdir();
                    }
                    //3.文件传输=================
                    //获取文件上传的流
                    InputStream inputStream=fileItem.getInputStream();
                    //创建一个文件输出流
                    //realPath=真实的文件夹
                    //差了一个文件,加上输出文件的名字+"/"+uuidFileName
                    FileOutputStream fos=new FileOutputStream(realPath+"/"+fileName);
                    //创建一个缓冲区
                    byte[] buffer=new byte[1024*1024];
                    //判断是否读取完毕
                    int len=0;
                    //如果大于0说明还存在数据
                    while((len=inputStream.read(buffer))>0){
                        fos.write(buffer,0,len);
                    }
                    msg="上传文件成功";
                    //关闭流
                    fos.close();
                    inputStream.close();
                    fileItem.delete();//上传文件成功,清除临时文件

                }

            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  msg;
    }
    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

    }
}

运行结果

在这里插入图片描述

如果对您有帮助,免费的赞点一个~感谢🙏

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值