文件上传与下载

本文探讨了文件上传的两种方式,包括ApacheCommonsFileUpload和Servlet3.1内置功能,以及Web3.0中MultipartConfig和Part接口的应用。同时介绍了文件上传的特殊要求和限制,以及文件下载的简单步骤。
摘要由CSDN通过智能技术生成

文件上传与下载

1. 文件上传

为了能上传文件,必须将表单的 method 设置为 POST,并将 enctype 设置为 multipart/form-data

有两种实现文件上传的方式:

  • 底层使用 Apache Commons FileUpload 包

  • 底层使用 Servlet 3.1 内置的文件上传功能

无论是哪种方式,其使用方式都是一样的,将 file 类型的请求参数绑定到请求处理方法的特定类型的参数上:

  • CommonsMultipartFile(commons-fileupload)

  • MultipartFile(Servlet 3.1)

Web 3.0 的文件上传

普通的表单(form)元素无法直接上传文件,必须通过「特殊处理」。

对上传文件功能而言,有些特殊的地方:

  • form 表单内,要添加控件 <input type="file" name="myfile">
  • form 表单的提交方式必须是 post 方式
  • form 表单的内容格式要定义成 multipart/form-data 格式
<form action="..." method="post" enctype="multipart/form-data">
  ...
</form>

enctype="multipart/form-data" 表示表单元素中的 input 数据以二进制的方式发送到服务端。此时如果是普通的 input 数据,无法像之前一样从 request 中直接获得。

对于上传文件的的大小问题,惯例是:

  • 足够小的文件,先接收到内存中,最后写入磁盘。
  • 稍大的文件,写入磁盘临时文件,最后写入最终目的地。
  • 大型文件,禁止上传。

在 Web 3.0 之前 使用 commons-fileupload 库是最常见的上传办法。当 Servlet 的设计者意识到文件上传的重要性后,在 Web 3.0 中它就成了一项内置的特性。

Web 3.0 中的文件上传主要围绕着 MultipartConfig 注解和 Part 接口。

@MultipartConfig 注解
属性说明
fileSizeThreshold
可选属性
超过该值大小的文件,在上传过程中,将被写入磁盘临时文件,而不是保存在内存中。
maxFileSize
可选属性
每个上传文件的大小上限。
maxRequestSize
可选属性
一次请求(可能包含多个上传)的大小上限。
@WebServlet(urlPatterns="/hello.do")
@MultipartConfig(maxFileSize = 5*1024*1024)
public class HelloServlet extends HttpServlet  {
  ...
}
Part 接口

在一个表单(Form)中,无论是否有文件上传控件,Servlet 3.0 都会将这些表单控件对应成代码中的一个 Part 对象。

通过 request 对象的 .getParts() 方法可以获得所有的这些 Part 对象。

Collection<Part> parts = request.getParts();

在一个或多个部分组成的请求中,每一个表单域(包括非文本域),都将被转换成一个 Part 。

普通文本域和文件上传域的区别在于,其 Part 对象的 .getContentType() 方法返回值的不同。对于普通文本域的 Part 对象而言,该方法返回 null 。

for (Part part : parts) {
    if (part.getContentType() == null) {
        System.out.println("普通文本域");
    }
    else {
        System.out.println("文件上传域");
    }
}

补充,如果是要获取普通文本域的值,其实直接使用正常 request.getParameter() 就行。

每一个 Part 分为「头」和「体」两部分。普通文本域只有头部,而文件上传域则有头有体。

普通文本域的头部形式为:

content-disposition:form-data; name="域名"

上传文本域的头部形式为:

content-type:内容类型
content-disposition:form-data; name="域名"; filename="文件名"

对我们而言,需要的是文本上传域中的 content-disposition 中的 filename 部分。

String header = part.getHeader("content-disposition"); 
// 内容为 form-data; name="域名"; filename="文件名"

通常会使用工具类,将 content-disposition 中的 filename 中的值截取出来。

private String getFileName(String header) {

    String[] arr = header.split("; ");

    String item = null;
    for (String cur : arr) {
        // System.out.println("debug: " + cur);
        if (cur.startsWith("filename=")) {
            item = cur;
            break;
        }
    }

    int start = item.indexOf('"')+1;
    int end = item.lastIndexOf('"');

    String filename = item.substring(start, end);

    // System.out.println(filename);

    return filename;
}

Part 对象直接提供了方法将上传文件的内容写入盘:

String savePath = request.getServletContext().getRealPath("/WEB-INF/uploadFile/");
String filePathName = savePath + File.separator + fileName; // 目标文件路径名
part.write(filePathName); // 把文件写到指定路径

Part 的其它常用方法

方法说明
getContentType()获得 Part 的内容类型。如果 Part 是普通文本,那么返回 null 。
该方法可以用于区别是普通文本域,还是文件上传域。
getHeader()该方法用于获取指定的标头的值。
对于上传文本域的 Part,该参数有 content-type 和 content-disposition
对于普通文本域,则只有 content-disposition 一种。
getName()无论是普通文本域 Part ,还是文件上传域 Part ,都是获得域名值。
write()将上传文件写入磁盘中。
delete()手动删除临时文件
getInputStream()以 InputStream 形式返回上传文件的内容。
利用 commons-fileupload 文件上传

利用 commons-fileupload 文件上传需要利用引入 commons-fileupload 包(它依赖于 commons-io 包)

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

作为 Servlet 内置上传功能之前的『准标准』,Servlet 在引入内置上传功能时借鉴了 commons-fileupload 的实现方式。因此,在了解 Servlet 内置上传功能之后,再回头看 commons-fileupload 文件上传时,你会发现它们的基本逻辑/大道理时一样的,只不过 commons-fileupload 的实现会罗嗦一些

在 Servlet 内置的上传功能中,从 request 中获得的是一个 Part[],其中的每一个 Part 对象对应着表单中的一个表单域(Form Field)。而 commons-fileupload 中我们从 request 中获得的是一个 List<FileItem>,commons-fileupload 中使用 FileItem 来对应每一个表单域,起到和 Part 一样的作用。

commons-fileupload 的罗嗦体现在以下几个方面:

  • commons-fileupload 不能对 Servlet 使用注解,因此相关的上传配置需要通过编码实现。
  • commons-fileupload 不能使用 request.getParameter()

为了能够从 request 中获得 List<FileItem>,你需要两个对象:

// 创建上传所需要的两个对象
DiskFileItemFactory factory = new DiskFileItemFactory();  // 磁盘文件对象
ServletFileUpload sfu = new ServletFileUpload(factory);   // 文件上传对象

如果不做出设置,那么相关设置则采用默认值。

// 设置上传过程中所收到的数据是『存内存』还是『存磁盘』的阈值
factory.setSizeThreshold(100 * 1024); 
// 设置磁盘临时文件的保存目录
factory.setRepository(new File("D:/upload"));

// 设置解析文件上传中的文件名的编码格式,解决上传文件名中文乱码问题
sfu.setHeaderEncoding("utf-8");
// 限制单个文件的大小
sfu.setFileSizeMax(10*1024);
// 限制上传的总文件大小
sfu.setSizeMax(100*1024);

在创建文件上传对象(并作出相应设置)之后,我们可以通过它从 request 中获取我们所需要的 List<FileItem>

List<FileItem> list = sfu.parseRequest(request);

FileItem 自带了方法,可以判断当前的 FileItem 对应的是页面上的普通文本域,还是文件上传域:

for (FileItem item : list) {
    if (item.isFormField()) {
        System.out.println("普通文本域");
    }
    else {    
        System.out.println("文件上传域");
    }
}

由于 commons-fileupload 中无法使用 request.getParameter(),因此,为了获得普通文本域中的数据,需要使用 FileItem 自己的方法:

for (FileItem item : list) {
    if (item.isFormField()) {
        String fieldName = item.getFieldName();         // 例如:username / password
        String fieldValue = item.getString("UTF-8");    // 例如,tom / 123456
        System.out.println(fieldName + ": " + fieldValue);
    }
    else {    
        System.out.println("文件上传域");
    }
}

由于 commons-fileupload 引用了 commons-io,所以,将上传的文件内容写入磁盘倒是十分简单:

for (FileItem item : list) {
    if (item.isFormField()) {
        ...
    }
    else {    
        System.out.println("文件上传域");

        // 创建输出文件
        String name = item.getName();// 获取上传文件的名字
        String outPath = "D:/upload/" + name;
        FileOutputStream output = new FileOutputStream(new File(outPath));

        // 获得上传文件字节流
        InputStream input = item.getInputStream(); 

        // 使用 IOUtils 工具将输入流中的数据写入到输出流。
        IOUtils.copy(input, output);

        System.out.println("上传成功!保存的路径为:" + outPath);
        input.close();      // 关闭输入流
        output.close();     // 关闭输出流
        item.delete();      // 删除处理文件上传时生成的临时文件
    }
}

2. 文件下载

内容类型文件扩展名描述
text/plaintxt文本文件(包括但不仅包括txt)
application/msworddocMicrosoft Word
application/pdfpdfAdobe Acrobat
application/zipzipwinzip
audio/mpegmp3mp3 音频文件
image/gifgifCOMPUSERVE GIF 图像
image/jpegjpeg jpgJPEG 图像
image/pngpngPNG 图像

详细 MIME 参见 网址

相对于上传而言,下载文件较为简单,只需要完成两步:

  1. 设置响应的内容类型。
  2. 添加一个 content-disposition 到响应标头(addHeader()方法),其值为:attachment; filename=文件名
  3. 通过 resp 对象获得输出流,将文件内容发送至客户端。
resp.setContentType("text/plain");      // step 1
resp.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("D:/note.txt", "UTF-8")); // step 2

InputStream is = new FileInputStream(new File("D:/note.txt"));
OutputStream out = resp.getOutputStream();

byte[] buffer = new byte[1024];
int n = 0;
while ((n = is.read(buffer)) > 0) {
  out.write(buffer, 0, n);            // step 3
}

is.close();
out.close();
System.out.println("下载成功");
  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JSP(JavaServer Pages)文件下载是在web开发中常用的功能,主要用于实现用户上文件到服务器或下载服务器上存储的文件文件的过程主要分为以下几个步骤: 1. 前端页面显示上表单,包括一个文件选择框和一个提交按钮。 2. 用户在文件选择框中选择要上文件。 3. 用户点击提交按钮后,前端将选择的文件发送到后台的JSP页面。 4. 后台的JSP页面通过request对象获取用户上文件,并将其保存到服务器的特定位置。 文件下载的过程主要分为以下几个步骤: 1. 前端页面提供下载链接或按钮。 2. 用户点击下载链接或按钮后,前端发送请求到后台的JSP页面。 3. 后台的JSP页面根据请求参数或其他逻辑,将需要下载文件读取或获取到。 4. 后台将获取到的文件通过response对象返回给前端。 5. 前端接收到文件数据后,根据文件的MIME类型进行相应的处理,比如保存到本地或直接在浏览器中打开等。 在JSP页面中,通过使用servlet API提供的request和response对象,结合一些Java IO的API,可以实现文件下载的功能。对于文件,需要注意在服务器端对文件大小、类型和异常处理进行合理的控制;对于文件下载,需要设置response的头部信息,以便浏览器正确识别文件类型并进行下载操作。 基于JSP文件下载的实现,可以方便地实现一些常见的功能,比如用户上头像、上附件、下载文档等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值