Java web

JavaWeb 文件上传和下载

2019-01-02  本文已影响0人  dawsonenjoy

一、文件上传

步骤

前端部分

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
上一篇下一篇

猜你喜欢

热点阅读