文件上传与下载与文件服务器知识概括

Posted GeorgeLin98

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文件上传与下载与文件服务器知识概括相关的知识,希望对你有一定的参考价值。

文件上传与下载与文件服务器知识概括

文件上传与下载

常见文件上传方式:

  • 我们知道,前端上传文件时,最常见的就是利用Form表单的方式进行文件的上传功能了,上传时设置enctype为multipart/form-data,或者直接利用jquery的一些上传插件实现。
  • 而后端,在jsp+Servlet时代,最常见的就是利用smartupload上传组件。而在SpringMvc时代,其提供了MultipartFile封装类它包含了文件的二进制流和文件相关属性(文件名、大小等)。所以在SpringBoot中也是使用此封装类进行文件上传的。
  • 上面说到,springmvc中是利用MultipartFile进行文件上传的。而MultipartFile是个接口类,它的实现类有CommonsMultipartFile和StandardMultipartFile。这里简单说明下:
    StandardMultipartFile:是基于j2ee自带的文件上传进行解析,即使用Servlet3.0提供的javax.servlet.http.Part上传方式。
    CommonsMultipartFile:是基于apache fileupload的解析。
  • 所以正常我们使用时,也是无需关系底层是以哪种方式进行文件上传处理的,SpringMvc会给我们做相应的转换的。
  • 链接:SpringBoot | 第十七章:web应用开发之文件上传

Servlet2.5 方式(原生apache fileupload方式):

  • 介绍一下使用到的几个核心类:
    ①DiskFileItemFactory – 设置磁盘空间,保存临时文件。只是一个工具类
    ②ServletFileUpload – 文件上传的核心类,此类接收 request,并解析
    ③ServletFileUpload.parseRequest(request); – List 解析 request
  • 流程:
    ①创建一个 DiskFileItemFactory 工厂类,并制定临时文件和大小
    ②创建 ServletFileUpload 核心类,接收临时文件,做请求的转换
    ③通过 ServletFileUpload 类转换原始请求,得到 FileItem 集合
    ④遍历集合中的各个元素并处理
    ⑤判断每个元素是否是普通表单项,如果是则按照普通表单项处理
    ⑥如果不是普通表单项,则是文件,通过处理的方式进行处理(上传)
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设定编码,可以获取中文文件名
        request.setCharacterEncoding("UTF-8");
        // 获取tomcat下的upload目录的路径
        String path = getServletContext().getRealPath("/upload");
        // 临时文件目录
        String tempPath = getServletContext().getRealPath("/temp");
        // 检查我们是否有文件上传请求
        // boolean isMultipart = ServletFileUpload.isMultipartContent(req);
        // 1、声明DiskFileItemFactory工厂类,用于在指定磁盘上设置一个临时目录
        DiskFileItemFactory disk = new DiskFileItemFactory(1024 * 10, new File(tempPath));
        // 2、声明ServletFileUpload,接收上面的临时文件。也可以默认值
        ServletFileUpload up = new ServletFileUpload(disk);
        // 3、解析request
        try {
            List<FileItem> list = up.parseRequest(request);
            if (list.size() > 0) {
                for (FileItem file : list) {
                    // 判断是否是普通的表单项
                    if (file.isFormField()) {
                        String fieldName = file.getFieldName();
                        // 中文乱码,此时还需要指定获取数据的编码方式
                        // String value = file.getString();
                        String value = file.getString("UTF-8");
                        System.out.println(fieldName + "=" + value);
                    } else { // 说明是一个文件
                        // 获取文件本身的名称
                        String fileName = file.getName();
                        System.out.println(file.getFieldName());
                        // 处理文件名称
                        fileName = fileName.substring(fileName.lastIndexOf("\\\\") + 1);
                        System.out.println("old Name : " + fileName);
                        // 修改名称
                        String extName = fileName.substring(fileName.lastIndexOf("."));
                        String newName = UUID.randomUUID().toString().replace("-", "") + extName;
                        // 保存新的名称,并写出到新文件中
                        file.write(new File(path + "/" + newName));
                        System.out.println("文件名是:" + fileName);
                        System.out.println("文件大小是:" + file.getSize());
                        file.delete();
                    }
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Servlet3.0 方式(StandardMultipartFile方式):

  • 使用注解 @MultipartConfig 将一个 Servlet 标识为支持文件上传。Servlet3.0 将multipart/form-data 的 POST 请求封装成 Part,通过 Part 对上传的文件进行操作。
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("上传文件...");
        // 设置编码
        request.setCharacterEncoding("UTF-8");
        // 获取普通表单项参数
        String uname = request.getParameter("uname");
        System.out.println(uname);
        // 上传文件
        // 得到part对象 request.getpart(name):name代表的是表单中file元素的name属性值
        Part part = request.getPart("myfile");
        // 得到文件存放的路径
        String path = request.getServletContext().getRealPath("/");
        // 得到文件名
        String fileName = part.getSubmittedFileName();
        // 上传
        part.write(path + fileName);
    }

}

SpringMVC方式(CommonsMultipartFile方式):

@Controller 
public class FileController { 
    @RequestMapping("/uploadFile") 
    public ModelAndView uploadFile(HttpServletRequest request){ 
        ModelAndView mv=new ModelAndView(); 
        mv.setViewName("result"); 
        MultipartHttpServletRequest mr=(MultipartHttpServletRequest) request; 
        MultipartFile multipartFile= mr.getFile("file"); 
        String path=request.getSession().getServletContext().getRealPath("upload"); 
        System.out.println(path); 
        if(null!=multipartFile&&!multipartFile.isEmpty()){ 
            String fileName=multipartFile.getOriginalFilename(); 
        try { 
            multipartFile.transferTo(new File(path,fileName)); 
            mv.addObject("msg", "文件上传成功!"); 
        } catch (Exception e) { 
            mv.addObject("msg", "上传失败!"); 
            e.printStackTrace(); 
        } 
        } 
        return mv; 
    } 
}

原生SmartUpload方式:

 		//实例化组件
        SmartUpload smartUpload = new SmartUpload();
        //初始化上传操作
        smartUpload.initialize(this.getServletConfig(), request, response);
        try {
            //上传准备
            smartUpload.upload();
            //对于普通数据,单纯到request对象是无法获取得到提交参数的。也是需要依赖smartUpload
            String password = smartUpload.getRequest().getParameter("password");
            System.out.println(password);
            //上传到uploadFile文件夹中
            smartUpload.save("uploadFile");
        } catch (SmartUploadException e) {
            e.printStackTrace();
        }

MultipartFile与CommonsMultipartFile与File的区别:

  • MultipartFile与CommonsMultipartFile:
    ①本质区别:MultipartFile 是接口, CommonsMultipartFile 是其实现类
    ②使用区别:CommonsMultipartFile 类型需要添加@RequestParam 参考博客
    ③建议使用: MultipartFile,原因一般情况都是使用接口,该接口可能会增加实现类,如此就不需要去修改代码中的类型;并且目前来说MultipartFile使用时不需要添加注解,比较方便。
  • MultipartFile与File:
    ①MultipartFile是spring类型,代表html中form data方式上传的文件,包含二进制数据+文件名称。

多种文件上传方式简介:

  • 多文件上传时和单文件上传时前端都是一个字段,因此没有区别,只不过多文件上传时弹出时选择多个文件就ok了。
@ResponseBody
    @RequestMapping(value = "uploadfiles", produces = "multipart/form-data;charset=UTF-8")
    public String uploadfile(MultipartHttpServletRequest request){
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (multipartResolver.isMultipart(request)) {
            //上传多个文件,一个字段中包含多个文件
            String projectPath = request.getSession().getServletContext().getRealPath("/");
            MultiValueMap<String, MultipartFile> multiFileMap = request.getMultiFileMap();
            List<MultipartFile> fileSet = new LinkedList<>();
            for(Map.Entry<String, List<MultipartFile>> temp : multiFileMap.entrySet()){
                fileSet = temp.getValue();
            }

            for(MultipartFile temp : fileSet){
                File file2 = new File(projectPath+temp.getOriginalFilename());
                try {
                    temp.transferTo(file2);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return "";
    }
//多个文件的上传
    @PostMapping("/uploads")
    public String uploads(MultipartFile[] uploadFiles, HttpServletRequest request) {
        //1,对文件数组做判空操作
        if (uploadFiles == null || uploadFiles.length < 1) {
            return "文件不能为空";
        }
        //2,定义文件的存储路径,
        String realPath = request.getSession().getServletContext().getRealPath("/uploadFile/");
        File dir = new File(realPath);
        if (!dir.isDirectory()) {//文件目录不存在,就创建一个
            dir.mkdirs();
        }
        try {
            String filePathS = "";
            //3,遍历文件数组,一个个上传
            for (int i = 0; i < uploadFiles.length; i++) {
                MultipartFile uploadFile = uploadFiles[i];
                String filename = uploadFile.getOriginalFilename();
                //服务端保存的文件对象
                File fileServer = new File(dir, filename);
                System.out.println("file文件真实路径:" + fileServer.getAbsolutePath());
                //2,实现上传
                uploadFile.transferTo(fileServer);
                String filePath = request.getScheme() + "://" +
                        request.getServerName() + ":"
                        + request.getServerPort()
                        + "/uploadFile/" + filename;
                filePathS = filePathS + "\\n" + filePath;
            }
            //4,返回可供访问的网络路径
            return filePathS;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "上传失败";
    }
  • 多文件多字段上传
@ResponseBody
    @RequestMapping(value = "uploadfiles", produces = "multipart/form-data;charset=UTF-8")
    public String uploadfile(MultipartHttpServletRequest request){
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (multipartResolver.isMultipart(request)) {
            //上传多个文件,每个字段一个文件
            Iterator<String> fileNames = request.getFileNames();
            while (fileNames.hasNext()){
                // 取得上传文件
                String uploadName = fileNames.next();
                MultipartFile file = request.getFile(uploadName);
                if (file != null) {
                    String projectPath = request.getSession().getServletContext().getRealPath("/");
                    String originalFilename = file.getOriginalFilename();
                    String tmpFile = projectPath + System.currentTimeMillis() + "_" + originalFilename;
                    File targetFile = new File(tmpFile);
                    try {
                        file.transferTo(targetFile);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return "";
    }

大文件上传,秒传、断点续传、分片上传:

  • 什么是秒传:
    ①通俗的说,你把要上传的东西上传,服务器会先做MD5校验,如果服务器上有一样的东西,它就直接给你个新地址,其实你下载的都是服务器上的同一个文件,想要不秒传,其实只要让MD5改变,就是对文件本身做一下修改(改名字不行),例如一个文本文件,你多加几个字,MD5就变了,就不会秒传了.
  • 什么是分片上传:
    ①分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。
    ②分片上传的场景:
    <1>大文件上传
    <2>网络环境环境不好,存在需要重传风险的场景
  • 什么是断点续传:
    ①断点续传是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传或者下载未完成的部分,而没有必要从头开始上传或者下载。本文的断点续传主要是针对断点上传场景。
    ②应用场景:
    <1>断点续传可以看成是分片上传的一个衍生,因此可以使用分片上传的场景,都可以使用断点续传。
  • 链接:大文件上传:秒传、断点续传、分片上传

上传文件后的处理方式:

  • 通过流的方式直接处理
  • 采用file.transferTo()方法

文件上传原理:

  • 步骤:
    ①读取 http body 部分
    ②根据 boundary 分析出分隔符特征(这个串是唯一的,不会与body内其他数据冲突)
    ③根据实际分隔符分段获取 body 内容
    ④遍历分段内容根据 Content-Disposition 特征获取其中值
    ⑤根据值中 filename 或 name 区分是否是包含二进制流还是表单数据的 k-v
    ⑥根据 filename 获取原始文件名
    ⑦从连续 两个 newline 字符串为起始至当前分段完毕,按照二进制流读取上传文件流信息。
    ⑧完成后即有:
    <1>原始文件名信息
    <2>原始文件类型信息
    <3>全部文件流信息
  • 文件上传为什么要用 multipart/form-data不使用旧有的application/x-www-form-urlencoded?
    ①因为此类型不适合用于传输大型二进制数据或者包含非ASCII字符的数据。平常我们使用这个类型都是把表单数据使用url编码后传送给后端,二进制文件当然没办法一起编码进去了。所以multipart/form-data就诞生了,专门用于有效的传输文件。
  • 可以用 application/json吗?
    ①无论你用什么都可以传,只不过会要综合考虑一些因素的话,multipart/form-data更好。例如我们知道了文件是以二进制的形式存在,application/json 是以文本形式进行传输,那么某种意义上我们确实可以将文件转成例如文本形式的 Base64 形式。但是呢,你转成这样的形式,后端也需要按照你这样传输的形式,做特殊的解析。并且文本在传输过程中是相比二进制效率低的,那么对于我们动辄几十M几百M的文件来说是速度是更慢的。

Java下载文件的几种方式:

  • 简介:
    Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
    ②格式说明: content-disposition = “Content-Disposition” “:” disposition-type *( “;” disposition-parm )  
    ③字段说明:Content-Disposition为属性名disposition-type是以什么方式下载,如attachment为以附件方式下载disposition-parm为默认保存时的文件名服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment:复制代码 代码如下:
Response.AppendHeader("Content-Disposition","attachment;filename=FileName.txt");
  • 以流的方式下载.:
public HttpServletResponse download(String path, HttpServletResponse response) {
        try {
            // path是指欲下载的文件的路径。
            File file = new File(path);
            // 取得文件名。
            String filename = file.getName();
            // 取得文件的后缀名。
            String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();
 
            // 以流的形式下载文件。
            InputStream fis = new BufferedInputStream(new FileInputStream(path));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer)

以上是关于文件上传与下载与文件服务器知识概括的主要内容,如果未能解决你的问题,请参考以下文章

文件的上传与下载

Android文件上传与下载

文件上传与下载

Linux系统上传文件与下载文件命令

我的代码库-Java8实现FTP与SFTP文件上传下载

JavaScript大文件上传解决方案实例代码