• JavaWeb 文件的上传、下载


    文件上传

    表单

    <form action="HandlerServlet" method="post" enctype="multipart/form-data">
    请选择要上传的文件:<input type="file" name="uploadFile" accept="*/*" multiple /><br />
    <button type="submit">上传</button>
    </form>

    指定<form>的method="post", enctype="multipart/form-data"。

    accept指定文件类型,有多种类型时逗号分隔,multiple指定可以选择多个文件。

    传统处理方式

    ServletInputStream inputStream = request.getInputStream();

    选择的文件是放在请求消息体中的。获取的输入流中包含了上传的所有文件,如果单个上传还好处理,如果同时上传多个,不好从中分离出单个文件的数据,且自己写代码获取文件名很麻烦。

    Commons-FileUpload

    Commons-FileUpload是Apache的开源组件,提供了一套处理上传文件的API。

    使用FileUpload需要2个jar包:

    • commons-fileupload.jar     
    • commons-io.jar    这是FileUpload依赖的IO包,要单独下载添加。

    FileUpload组件主要的接口、类:

    • DiskFileItemFactory类     将请求消息实体中的每一个参数都封装为单独的FileItem对象
    • ServletFileUpload类     调用DiskFileItemFactory类来封装请求参数,并对封装好的FileItem做进一步处理,以List<FileItem>形式返回
    • FileItem接口   用于封装请求参数,提供了一系列操作上传文件的方法。

     

    DiskFileItemFactory类的常用方法

    • DiskFileItemFactory()    //空参的构造方法,使用默认临界值、系统临时文件夹
    • DisFileItemfactory(int  size, File  repository)   //带参的构造方法,指定临界值、缓存目录
    • FileItem  createItem()   //将一个请求参数封装为FileItem,并返回封装好的FileItem。这个方法由DiskFileItemFactory内部自动调用。不用我们管。
    • set|SizeThreshold(int  size)   //设置临界值
    • setRepository(File  repository)    //设置缓存目录。这2个方法都要对应的get方法。

    JVM的内存是有限的,比如用户上传一个1G的文件,直接保存在JVM内存中,实打实地占用服务器内存,这绝对不行。

    临界值指定文件大小,小于临界值的文件直接存储在JVM内存中,大于临界值的文件,以临时文件(.tmp)的形式保存在缓存目录中。默认为10240,默认单位Byte,即10KB。

    缓存目录可以指定为磁盘上的某一个文件夹,以File形式指定,不指定时默认使用系统的临时文件夹。

    ServletFileUpload类的常用方法

    • ServletFileUpload()    //空参构造器
    • ServletFileUpload(FileItemFactory fileItemFactory)   //带参的构造器,指定使用的FileItemFactory对象。FileItemFactory是接口,一般使用它的实现类DiskFileItemFactory(上面那个类)。
    • setFileItemFactory(FileItemFactory fileItemFactory)
    • setSizeMax(long  size)   //设置请求消息实体内容(传递的所有请求参数)的最大尺寸,防止用户恶意上传超大文件浪费服务器存储空间。默认单位Byte。
    • setFileSizeMax(long size)   //设置单个文件的最大尺寸
    • setHeaderEncoding("utf-8")    //设置请求参数的字符集,未设置时使用HttpServletRequest设置的字符集,若HttpServletRequest也未设置字符集,则使用系统默认的字符集。

    以上几个setter()方法均有对应的getter()方法。

    • List<FileItem>   parseRequest(HttpServletRequest  request)    //调用FileItemFactory对象的方法,开始封装每个请求参数,以List<FileItem>形式返回封装好的所有的请求参数
    • FileItemIterator  getItemIterator(HttpServletRequest  request)   //同上,只是返回的是迭代器

    注意:是封装每一个请求参数,不是只封装上传的文件,所以有些FileItem保存的是普通的请求字段。

    FileItem接口常用的方法

    •  boolean  isFormField()    //判断这个FileItem封装的是不是普通的表单字段
    • String getFieldName()   //获取字段名,即表单控件的name属性
    • String  getName()   //获取上传文件的文件名(带有后缀,比如:1.jpg),如果封装的是普通的表单字段,得到是null
    • long getSize()   //获取文件的大小,默认单位字节
    • String getContentType()   //获取文件类型,比如image/jpeg
    • boolean  isInMemory()    //是否存储在JVM内存中。如果存储在内存中,返回true;存储在缓存目录中,返回false。
    • void  write(File  file)    //将上传文件写到服务器的磁盘中。如果此文件是存储在缓存目录中的,写完会自动删除缓存目录中对应的临时文件。
    • InputStream getInputStream()   //获取输入流
    • void  delete()  //清除FileItem存储的主体内容,如果存储在缓存目录中,会删除缓存目录中对应的临时文件。虽然GC会自动清除不再使用的临时文件,但使用完立即删除是最好的。

    Servlet处理上传文件示例

     1 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     2         response.setContentType("text/html;charset=utf-8");
     3 
     4         //创建FileItemFactory对象
     5         DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
     6         File tempFolder=new File("E:/tempFolder");   //Java中的File即指文件,又指文件夹
     7         if (!tempFolder.exists()){  //不存在就新建
     8             tempFolder.mkdir();
     9         }
    10         //指定缓存目录
    11         diskFileItemFactory.setRepository(tempFolder);  
    12 
    13         //创建ServletFileUpload对象
    14         ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
    15         //指定编码字符集
    16         servletFileUpload.setHeaderEncoding("utf-8");
    17 
    18         try {
    19             //获取List<FileItem>
    20             List<FileItem> fileItems = servletFileUpload.parseRequest(request);
    21             for (FileItem fileItem:fileItems){
    22                 if(!fileItem.isFormField()){  //是上传文件
    23                     //使用UUID创建唯一的文件名
    24                     String fileName= UUID.randomUUID().toString()+"_"+fileItem.getName();
    25                     String path="C:/Users/chy/Desktop/upload/"+fileName;
    26                     fileItem.write(new File(path));  //写到服务器磁盘上。写完会自动删除临时文件。
    27                 }
    28             }
    29 
    30         } catch (FileUploadException e) {
    31             e.printStackTrace();
    32         } catch (Exception e) {
    33             e.printStackTrace();
    34         }
    35 
    36 
    37     }

     防止文件重名,有2种方式

    • 在文件名中加时间戳(毫秒)。当并发量很大时,依然可能出现重名。
    • 在文件名中加UUID.randomUUID().toString() 。

    UUID即通用唯一识别码,能唯一标识某个东西。

    UUID产生的这个字符串包含32个十六进制数,用4根连词线-分为5段,示例: bd95572b-7fcf-46b2-ae3e-6087d66db40f ,8-4-4-4-12的形式。

    文件下载

    最简单的方式:使用<a>标签直接链接到文件地址。

    <a href="http://www.xxx.com/resource/特斯拉.mp4">下载xxx</a>

    有2个问题:

    • 某些类型的文件,比如jpg、mp3、mp4、txt等会直接在网页中打开、播放,而非下载。
    • 如果以后资源地址换了,比如换ftp服务器了、改资源路径了,需要一个个地修改<a>连接的href属性,十分麻烦。

    解决方式:

    (1)链接到Servlet,并传递文件名。

    <a href="downloadServlet?fileName=特斯拉.mp4">下载xxx</a>

    (2)Servlet

     1 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     2         request.setCharacterEncoding("utf-8");
     3         response.setContentType("text/html;charset=utf-8");
     4 
     5         String fileName = request.getParameter("fileName");
     6         //对文件名进行iso8859-1编码
     7         String browerFileName=new String(fileName.getBytes("utf-8"),"iso8859-1");
     8 
     9         //指定浏览器行为为下载
    10         response.addHeader("Content-Type","application/octet-stream");
    11         //指定下载文件名。注意这两个都是addHeader(),不是setHeader()
    12         response.addHeader("Content-Disposition","attachment;filename="+browerFileName);
    13 
    14         InputStream is = getServletContext().getResourceAsStream("resource/"+fileName);  //换路径时只需修改此句中的路径,不用修改每个<a>
    15         ServletOutputStream os= response.getOutputStream();
    16         byte[] buffer = new byte[1024];
    17         int len;
    18         while ((len=is.read(buffer))!= -1){  //注意是-1,不是0
    19             os.write(buffer);
    20         }
    21 
    22     }

    红字是为了解决下载文件名中文乱码。

    因为我们使用的是utf-8字符集,而chrome这些浏览器默认使用 iso8859-1 字符集。要转换一下,这样浏览器在设置下载文件名时才能识别文件名中的中文。

    其实 <a href="downloadServlet?fileName=特斯拉.mp4">下载xxx</a> ,url中的中文也要转换一下,一个中文字符转换为%xxxx的形式(%后面跟着4个十六进制数)。

    <a href="downloadServlet?fileName=<%=URLEncoder.encode("特斯拉.mp4","utf-8")%>">下载</a>

    String  URLEncoder.encode(String  str, String  charset)    //静态方法,返回编码后的字符串

    当然,不转换也行,都能正确识别。

  • 相关阅读:
    zoj 3620 Escape Time II dfs
    zoj 3621 Factorial Problem in Base K 数论 s!后的0个数
    bzoj 1789: [Ahoi2008]Necklace Y型项链 贪心
    BZOJ 4027: [HEOI2015]兔子与樱花 树上dp
    Codeforces Beta Round #80 (Div. 1 Only) D. Time to Raid Cowavans 分块
    图论:单源最短路与多源最短路问题
    图论:图的概念与图的储存方式
    hdu 4802 GPA 水题
    Codeforces Round #202 (Div. 1) A. Mafia 贪心
    Spring Bean配置默认为单实例 pring Bean生命周期
  • 原文地址:https://www.cnblogs.com/chy18883701161/p/11417710.html
Copyright © 2020-2023  润新知