JavaWeb 13 文件的上传和下载

1、文件的上传

概念

将本地文件上传到远程服务器上,文件是以一个流的形式上传到服务器中,这就需要用表单提交

对表单的要求

⑴ 表单的请求方式必须是POST
⑵ 表单的enctype属性必须是multipart/form-data
    ① 当该属性值为application/x-www-form-urlencoded时,在提交表单之前对表单中的请求参数进行URL编码。这是默认值
    ② 当该属性值为multipart/form-data,在使用包含文件上传控件的表单时,必须使用该值。表单中的表单项会以一个多部件的形式发送到服务器,一个表单项就是一个多部件

⑶ 表单项input中的type属性是file

2、Fileupload

Fileupload是Apache提供的专门用于进行文件上传和下载的工具

需要导入两个jar包:
    commons-fileupload-1.3.2-bin
    commons-io-2.5-bin

源码:
    commons-fileupload-1.3.2-src 源码
    commons-io-2.5-src 源码

DiskFileItemFactory类

文件项目工厂类

构造方法

public DiskFileItemFactory() {}
创建一个工厂类的实例

ServletFileUpload类

文件解析器

构造方法

public ServletFileUpload(FileItemFactory fileItemFactory) {}
使用提供的工厂创建一个解析器实例

parseRequest

public List parseRequest(HttpServletRequest request) {}
处理从表单提交过来的multipart/form-data流。传入request对象,得到表单提交过来的所有表单项

setFileSizeMax

public void setFileSizeMax(long fileSizeMax) {}
限制上传的单个文件的大小

setSizeMax

public void setSizeMax(long sizeMax) {}
限制上传的多个文件的大小

FileItem接口

解析请求之后,每个表单项(多部件)就是一个FileItem对象

isFormField

boolean isFormField();
是否是一个文本输入框(type=”text”)。如果是文件框(type=”file”),返回false

getFieldName

String getFieldName();
获取表单项的name属性值

getString

String getString(String encoding) throws UnsupportedEncodingException;
以指定的编码解析文本输入框(type=”text”)的value属性值。一般传入”UTF-8”,以防中文乱码

getName

String getName();
获取文件框(type=”file”)上传的文件的名字

注意:针对IE5、IE6,调用该方法获取到的是上传文件的绝对路径

处理方式:
String fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
这样不管有没有文件分隔符(/ 或 \),都可以获取文件名

getSize

long getSize();
获取文件框(type=”file”)上传的文件的大小

getContentType

String getContentType();
获取文件框(type=”file”)上传的文件的MIME类型

write

void write(File file) throws Exception;
将指定文件写到服务器的硬盘上。传入指定服务器路径的文件对象

上传示例

【index.jsp】

<h1>文件的上传</h1>
<h2><font color="red">${requestScope.errorMsg}</font></h2>
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username" /><br /><br />
    文件1:<input type="file" name="file1" /><br /><br />
    文件2:<input type="file" name="file2" /><br /><br />
    文件3:<input type="file" name="file3" /><br /><br />
    <input type="submit" value="上传" />
</form>

【success.jsp】

<h1>文件上传成功!</h1>
<a href="${pageContext.request.contextPath}/index.jsp">返回上传页面</a>

【FileUploadServlet】

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class FileUploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 创建工厂实例
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 创建解析器实例
        ServletFileUpload fileUpload = new ServletFileUpload(factory);

        // 设置单个文件大小5Kb【单位字节】
        fileUpload.setFileSizeMax(5 * 1024);
        // 设置多个文件大小2Mb【单位字节】
        fileUpload.setSizeMax(2 * 1024 * 1024);

        try {
            // 解析请求获取每一个表单项
            List<FileItem> fileItems = fileUpload.parseRequest(request);

            int size = fileItems.size();
            for (int i = 0; i < size; i++) {
                FileItem fileItem = fileItems.get(i);

                // 获取表单项的name属性值
                String fieldName = fileItem.getFieldName();

                // 是否是一个文本输入框
                boolean formField = fileItem.isFormField();
                if (formField) {
                    // 获取表单value属性值,以UTF-8来解码
                    String value = fileItem.getString("UTF-8");

                    // 没有输入内容
                    if ("".equals(value)) {
                        request.setAttribute("errorMsg", "请输入内容!");
                        request.getRequestDispatcher("/index.jsp").forward(request, response);
                        return;
                    }

                    System.out.println("表单项name属性:" + fieldName);
                    System.out.println("用户输入的内容:" + value);
                } else {
                    System.out.println("表单name属性值:" + fieldName);

                    // 获取文件名
                    String fileName = fileItem.getName();
                    // 获取文件大小(单位字节)
                    long fileSize = fileItem.getSize();
                    // 获取文件的MIME类型
                    String fileType = fileItem.getContentType();

                    // 没有上传文件
                    if (0 == fileSize) {
                        request.setAttribute("errorMsg", "请上传文件!");
                        request.getRequestDispatcher("/index.jsp").forward(request, response);
                        return;
                    }

                    System.out.println("文件名:" + fileName);
                    System.out.println("文件大小:" + fileSize + "字节");
                    System.out.println("文件MIME类型:" + fileType);

                    String uploadPath = "/upload";
                    // 上传到服务器的路径的绝对路径
                    uploadPath = getServletContext().getRealPath(uploadPath);
                    File uploadFile = new File(uploadPath);
                    if (!uploadFile.exists()) {
                        uploadFile.mkdirs();
                    }

                    // 兼容IE5、IE6获取到的文件名是绝对路径
                    fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);

                    // 生成UUID
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    // 唯一的文件名
                    uploadPath = uploadPath + File.separator + uuid + "_" + fileName;

                    uploadFile = new File(uploadPath);

                    // 写到服务器硬盘上
                    fileItem.write(uploadFile);

                }

            }

            // 成功,跳转到成功页面
            response.sendRedirect(request.getContextPath() + "/success.jsp");

        } catch (FileSizeLimitExceededException e) {
            request.setAttribute("errorMsg", "单个文件最大5Kb");
            request.getRequestDispatcher("/index.jsp").forward(request, response);

            System.out.println("单个文件错误信息:" + e.getMessage());
        } catch (SizeLimitExceededException e) {
            request.setAttribute("errorMsg", "多个文件最大2Mb");
            request.getRequestDispatcher("/index.jsp").forward(request, response);

            System.out.println("多个文件错误信息:" + e.getMessage());
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

3、文件上传的注意事项

⑴ 导入正确的包
    org.apache.commons.fileupload

不是
    org.apache.tomcat.util.http

⑵ 用户上传超过指定的单个文件大小,会报异常:

FileSizeLimitExceededException

超过多个文件大小,会报异常:

SizeLimitExceededException

在catch这些异常时,有时会给用户以响应,通过request域设置错误信息,但是当用户上传的文件超过限制的大小过大时,并不会转发,这是tomcat的bug

解决方式,是使用低版本的 tomcat【7.0.39】

详情请看:tomcat上传超过限制文件大小报异常而不能正常转发的bug

4、文件的下载

概念

将文件从远程服务器下载到本地

文件下载,只需将要下载的资源的地址放到超链接中,点击即可下载
但如果浏览器支持要下载的格式,将会直接打开,而且通过超链接下载,不能设置权限,所以一般将要下载的资源放到WEB-INF目录下,通过Servlet发请求来下载

具体步骤

⑴ 创建一个输入流,指向WEB-INF下的资源文件
Tips:可以通过ServletContext的getRealPath方法获取文件的真实路径

⑵ 设置响应头信息
    ① 设置文件的MIME类型
public void setContentType(String type);
传入文件的MIME类型

各种文件的MIME类型,可以从tomcat服务器的web.xml中搜索Default MIME Type Mappings查看

一些MIME:

    doc           application/msword
    jpg或jpeg     image/jpeg
    json          application/json
    mp3           audio/mpeg

    Tips:通过调用ServletContext的getMimeType方法获取文件的MIME类型
public String getMimeType(String file);
传入文件的绝对路径,可以获取文件的MIME类型

也可以告诉浏览器,这是一个文件流
response.setContentType(“application/octet-stream;charset=UTF-8”);

    ② 告诉浏览器如何处理下载的资源
response.setHeader(“Content-Disposition”, “attachment; filename=”+fileName);
告诉浏览器以附件的形式处理下载的资源
如果不设置,浏览器如果支持该资源格式,可能会自己打开资源文件

    Tips:需要先声明给浏览器响应时显示的文件的名字(fileName)

⑶ 创建一个输出流,将资源输出到浏览器
public ServletOutputStream getOutputStream() throws IOException;
获取输出流,用于给浏览器输出二进制数据。通过response对象来调用

⑷ 编写文件复制的代码
    可以声明byte类型的 数组(缓冲),int类型的 获取到的数据的长度,通过while循环来复制文件

也可以通过
    commons-io-2.5.jar包里的IOUtils类的copy方法
public static int copy(final InputStream input, final OutputStream output) throws IOException {}
传入InputStream和OutputStream,即可以复制文件

对中文文件名的处理

对于Chrome和IE浏览器

调用URLEncoder的encode方法
public static String encode(String s, String enc) {}
使用指定的编码机制将字符串转换为 application/x-www-form-urlencoded 格式

    URLEncoder.encode(fileName, “UTF-8”);
    这是固定写法,传入中文文件名,即可对其进行URL编码

对于火狐浏览器

对于火狐浏览器需要对中文文件名进行BASE64编码
需要调用sun.misc.BASE64Encoder的encode方法

    Tips:可以通过request.getHeader(“User-Agent”);,获取浏览器信息,再判断其中是否包含”Mozilla”和”Firefox”,即可判断是否是火狐浏览器

String fileName = "=?utf-8?b?"+new BASE64Encoder().encode(fileName.getBytes())+"?=";

    这是固定写法。需要事先声明中文文件名

通用方法

fileName = new String(fileName.getBytes(), “ISO-8859-1”);

注意事项

    声明的文件名需要带扩展名,否则下载的时候,会以没有扩展名的文件的形式下载

下载示例

【jsp页面的body内容】

<a href="${pageContext.request.contextPath}/download">下载资源!</a>

【FileDownloadServlet】

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

import sun.misc.BASE64Encoder;

public class FileDownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String filePath = "/WEB-INF/files/???.???";

        ServletContext servletContext = getServletContext();
        // 获取文件在服务器上的真实路径
        filePath = servletContext.getRealPath(filePath);

        // 输入流指向资源文件
        InputStream input = new FileInputStream(filePath);

        // 获取资源文件的MIME类型
        String mimeType = servletContext.getMimeType(filePath);

        // 设置响应资源文件的类型
        response.setContentType(mimeType);

        // 给浏览器响应时,显示的文件名
        String fileName = "???.???";

        // 获取浏览器信息
        String UA = request.getHeader("User-Agent");
        // 判断是否是火狐浏览器
        if (UA.contains("Mozilla") && UA.contains("Firefox")) {
            // 如果是火狐浏览器,需要对中文文件名进行BASE64编码
            fileName = "=?utf-8?b?" + new BASE64Encoder().encode(fileName.getBytes()) + "?=";
        } else {
            // 其他浏览器可以使用URL编码
            fileName = URLEncoder.encode(fileName, "UTF-8");
        }

        // 告诉浏览器以附件形式处理响应的资源文件
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

        // 获取输出流
        ServletOutputStream output = response.getOutputStream();

        // 复制文件
        IOUtils.copy(input, output);

        // 关闭流
        output.close();
        input.close();
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值