美文网首页Java web
JavaWeb 文件上传和下载

JavaWeb 文件上传和下载

作者: dawsonenjoy | 来源:发表于2019-01-02 22:01 被阅读0次

    一、文件上传

    步骤

    前端部分

    1.提供一个post方法的表单,并设置enctype属性(设置请求内容的MIME类型)为multpart/from-data,代表该表单支持文件上传
    2.提供一个文件上传框:<input type="file">
    举例:

    <form action="/xxx" method="post" enctype="multipart/form-data">
        <input type="text" name="description">
        <input type="file" name="file">
        <input type="submit">
    </form>
    
    后端部分

    将内容通过流读取进来,举例:

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        InputStream in = request.getInputStream();
        ServletOutputStream out = response.getOutputStream();
        int len = 0;
        byte[] b = new byte[1024];
        while((len = in.read(b))!=-1){
            out.write(b, 0, len);
        }
    }
    
    结果为:
    ------WebKitFormBoundary5x8Olc9cfCFQzCFd
    Content-Disposition: form-data; name="description"
    
    这是一个测试描述
    ------WebKitFormBoundary5x8Olc9cfCFQzCFd
    Content-Disposition: form-data; name="file"; filename="1.txt"
    Content-Type: text/plain
    
    这是1.txt的内容
    ------WebKitFormBoundary5x8Olc9cfCFQzCFd--
    

    fileupload

    是apache的commons组件提供的上传组件,主要用于解析上传的输入流内容,比如上面可以看出内容都是上传的各个表单内容的字节流,并且是混杂在一起的,而该组件则可以对这些流进行分类和处理。

    使用步骤

    1.添加相关jar包——commons-fileupload/commons-io(放到WEB-INF/lib下)
    2.实例化工厂类DiskFileItemFactory()对象
    3.实例化工厂解析类ServletFileUpload()对象
    4.调用解析类对象的parseRequest()方法进行解析,里面直接传入request对象即可,返回的是一个List<FileItem>对象(解析前可以先通过isMultipartContent()方法来判断传入的request对象是否支持文件上传)

    核心对象
    1.DiskFileItemFactory

    工厂类

    2.ServletFileUpload

    用于解析工厂对象的解析类

    3.FileItem

    代表表单项,比如下面这样就是一个FileItem:

    Content-Disposition: form-data; name="file"; filename="1.txt"
    Content-Type: text/plain
    
    这是1.txt的内容
    

    其主要提供了以下方法:
    (1)isFormField():判断是否为普通文本字段,如果返回false,表明是文件字段
    (2)getContentType():获取上传文件的类型
    其中对于普通表单项(textpassword等)提供了以下方法:
    (1)getFieldName():获取字段名
    (2)getString():获取值,可以传入编码参数来设置编码
    对于上传的文件提供了以下方法:
    (1)getInputStream():获取文件输入流
    (2)getName():获取文件名
    (3)getSize():获取文件大小
    (4)write(File):把上传的文件内容保存到指定文件中

    上传并输出文件示例
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            {
        DiskFileItemFactory factory = new DiskFileItemFactory();  //实例化工厂对象
        ServletFileUpload fileupload = new ServletFileUpload(factory);  //实例化工厂解析对象
        try {
            List<FileItem> list = fileupload.parseRequest(request);  //解析工厂
            for (FileItem f : list) {
                if (!f.isFormField()) {  //当为文件字段
                    String directoryname = this.getServletContext().getRealPath("/upload");
                    File directory = new File(directoryname);
                    if(!directory.exists() || !directory.isDirectory()){  //创建上传目录
                        directory.mkdirs();
                    }
                    System.out.println(f.getFieldName());   //file
                    System.out.println(f.getName()); // xxx.jpg,只有文件名
                    System.out.println(f.getContentType()); //image/jpeg
                    InputStream in = f.getInputStream();
                    ServletOutputStream out = response.getOutputStream();
                    FileOutputStream file = new FileOutputStream(new File(directoryname, f.getName()));  //根据传的文件名命名
                    int len = 0;
                    byte[] b = new byte[1024];
                    while ((len = in.read(b)) != -1) {
                        out.write(b, 0, len);  //输出图像
                        file.write(b, 0, len);  //保存文件
                    }
                    file.close();  //关闭文件
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    文件上传路径名问题

    可以看到上面传输的文件只会获得文件名,但在一些如IE浏览器中容易出现传输的文件名带路径的问题,如:C:/XXX/xxx.xx,此时上面的代码就会出错。因此可以使用commons-io包下FilenameUtils类提供的静态方法getName(),其对于不管是否带路径的文件字符串,都只会获取文件名部分的字符串,举例:

    System.out.println(FilenameUtils.getName("c:/sda/fdg/s"));  //s
    System.out.println(FilenameUtils.getName("c:/sda/fdg/s.fs"));  //s.fs
    System.out.println(FilenameUtils.getName("s.de"));  //s.de
    System.out.println(FilenameUtils.getName("fdg/s"));  //s
    System.out.println(FilenameUtils.getName("/sda/fdg/s"));  //s
    
    文件上传问题
    1.注入问题

    对于上面那段代码,会在项目下新建一个upload文件夹,并生成对应文件,此时访问:http://127.0.0.1:8080/项目名/upload/文件名即可看到刚才上传的文件内容。但是,此时要注意如果上传了像下面这样的文件:

    //xxx.jsp
    <%@ page contentType="text/html; charset=utf-8"%>
    <!DOCTYPE>
    <html>
    <head>
    <title></title>
    </head>
    <body>
        <%
            Runtime.getRuntime().exec("notepad");
        %>
    </body>
    </html>
    

    此时,在服务器上可能就会执行该notepad命令打开记事本,即在服务器上执行了用户上传的脚本文件
    解决方式:
    (1)将上传的文件放到用户无法访问到的文件路径下,如:WEB-INF
    (2)限制上传的文件类型,如jsp、exe、bat等

    2.文件覆盖问题

    对于在上传保存的路径下原有的文件,如果上传的文件和原有的文件重名,将可能发生原文件被覆盖的问题。
    解决方式:
    给文件名进行处理,如给文件名加上UUID使得名字唯一等

    3.文件夹文件内容过多问题

    解决方式:
    对文件夹目录等进行分类存储,如按日期/文件类别等

    4.文件内容过大

    解决方式:
    通过ServletFileUpload下的setFileSizeMax()方法限制单个文件大小,或者setSizeMax()设置总文件上传大小,或者通过getSize()获取文件大小后根据上传的文件大小进行处理,举例:

    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload fileupload = new ServletFileUpload(factory);
    fileupload.setSizeMax(1024 * 1024 * 3);  //文件大小总和不超过3M
    fileupload.setFileSizeMax(1024 * 1024);  //单个文件大小不超过1M
    try{
        ...
    } catch (FileUploadBase.SizeLimitExceededException e) {
        response.getWriter().print("文件总和内容过大!");
    } catch (FileUploadBase.FileSizeLimitExceededException e) {
        response.getWriter().print("文件内容过大!");
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    5.上传文件为空

    解决方式:
    判断上传的文件名是否为空

    6.产生临时文件

    在上传文件过程中往往会产生临时文件,导致磁盘空间不足,或者造成磁盘空间的浪费
    解决方式:
    (1)使用DiskFileItemFactory对象的setRepository(File)方法设置临时文件存储位置,举例:

    DiskFileItemFactory factory = new DiskFileItemFactory();
    factory.setRepository(new File("F:/"));  //在F盘下存放临时文件
    

    (2)通过FileItem下的delete()方法将临时文件删除,举例:

    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload fileupload = new ServletFileUpload(factory);
    List<FileItem> list = fileupload.parseRequest(request);
    for (FileItem f : list) {
        ...
        f.delete();  //将临时文件删除
    }
    

    二、文件下载

    在返回头response中添加下面的头信息即可:

    response.setHeader("content-disposition", "attachment;filename=文件名" );
    response.setHeader("content-type", 文件类型);
    

    举例:

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        FileInputStream in = new FileInputStream(new File(this.getServletContext().getRealPath("/img/测试图片.jpg")));
        ServletOutputStream out = response.getOutputStream();
        int len = 0;
        byte[] b = new byte[1024];
        response.setHeader("content-disposition", "attachment;filename=" + new String("测试.jpg".getBytes("utf-8"), "iso-8859-1"));  //避免文件名乱码
        response.setHeader("content-type", "image/jpeg");
        while((len = in.read(b)) != -1){
            out.write(b, 0, len);
        }
    }
    

    注:
    对于上面的文件类型是手动设置十分不便,因此可以用ServletContext下的getMimeType(文件名)方法来根据文件名自动获得文件类型,举例:

    response.setContentType(this.getServletContext().getMimeType("xxx.jpg"));  //自动设置为image/jpeg
    

    相关文章

      网友评论

        本文标题:JavaWeb 文件上传和下载

        本文链接:https://www.haomeiwen.com/subject/tuyblqtx.html