news 2026/10/30 1:17:34

【JavaWeb】文件上传下载(213-221)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【JavaWeb】文件上传下载(213-221)

文件上传

213.文件上传-文件上传的介绍

1、要有一个 form 标签,method=post 请求
2、form 标签的 encType 属性值必须为 multipart/form-data 值
3、在 form 标签中使用 input type=file 添加上传的文件
4、编写服务器代码(Servlet 程序)接收,处理上传的数据

encType=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><formaction="http://192.168.31.74:8080/09_EL_JSTL/uploadServlet"method="post"enctype="multipart/form-data">用户名:<inputtype="text"name="username"/><br>头像:<inputtype="file"name="photo"><br><inputtype="submit"value="上传"></form></body></html>

214.文件上传-上传的http协议内容介绍

215.文件上传-上传用到的类和方法介绍

commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入。

需要导入两个 jar 包:
commons-fileupload-1.2.1.jar
commons-io-1.4.jar

API作用
ServletFileUpload 类用于解析上传的数据
FileItem 类表示每一个表单项
boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);判断当前上传的数据格式是否是多段的格式。
public List<> parseRequest(HttpServletRequest request)解析上传的数据
boolean FileItem.isFormField()判断当前这个表单项,是否是普通的表单项。还是上传的文件类型。true 表示普通类型的表单项,false 表示上传的文件类型
String FileItem.getFieldName()获取表单项的 name 属性值
String FileItem.getString()获取当前表单项的值
String FileItem.getName();获取上传的文件名
void FileItem.write( file );将上传的文件写到 参数 file 所指向抽硬盘位置 。

216.文件上传-使用fileupload解析上传的数据

importorg.apache.commons.fileupload.FileItem;importorg.apache.commons.fileupload.FileItemFactory;importorg.apache.commons.fileupload.disk.DiskFileItemFactory;importorg.apache.commons.fileupload.servlet.ServletFileUpload;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.File;importjava.io.IOException;importjava.util.List;publicclassUploadServletextendsHttpServlet{/** * 用来处理上传的数据 * @param req * @param resp * @throws ServletException * @throws IOException */@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{//1 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)if(ServletFileUpload.isMultipartContent(req)){// 创建FileItemFactory工厂实现类FileItemFactoryfileItemFactory=newDiskFileItemFactory();// 创建用于解析上传数据的工具类ServletFileUpload类ServletFileUploadservletFileUpload=newServletFileUpload(fileItemFactory);try{// 解析上传的数据,得到每一个表单项FileItemList<FileItem>list=servletFileUpload.parseRequest(req);// 循环判断,每一个表单项,是普通类型,还是上传的文件for(FileItemfileItem:list){if(fileItem.isFormField()){// 普通表单项System.out.println("表单项的name属性值:"+fileItem.getFieldName());// 参数UTF-8.解决乱码问题System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));}else{// 上传的文件System.out.println("表单项的name属性值:"+fileItem.getFieldName());System.out.println("上传的文件名:"+fileItem.getName());fileItem.write(newFile("e:\\"+fileItem.getName()));}}}catch(Exceptione){e.printStackTrace();}}}}

文件下载

217.文件下载-文件下载的实现


response.setHeader(“Content-Disposition”, “attachment; fileName=1.jpg”);
这个响应头告诉浏览器。这是需要下载的。而 attachment 表示附件,也就是下载的一个文件。fileName=后面, 表示下载的文件名。

完成上面的两个步骤,下载文件是没问题了。但是如果我们要下载的文件是中文名的话。你会发现,下载无法正确显示出正确的中文名。
原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码。

packagecom.atguigu.servlet;importorg.apache.commons.io.IOUtils;importsun.misc.BASE64Encoder;importjavax.servlet.ServletContext;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.URLEncoder;publicclassDownloadextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 1、获取要下载的文件名StringdownloadFileName="2.jpg";// 2、读取要下载的文件内容 (通过ServletContext对象可以读取)ServletContextservletContext=getServletContext();// 获取要下载的文件类型StringmimeType=servletContext.getMimeType("/file/"+downloadFileName);System.out.println("下载的文件类型:"+mimeType);// 4、在回传前,通过响应头告诉客户端返回的数据类型resp.setContentType(mimeType);// 5、还要告诉客户端收到的数据是用于下载使用(还是使用响应头)// Content-Disposition响应头,表示收到的数据怎么处理// attachment表示附件,表示下载使用// filename= 表示指定下载的文件名// url编码是把汉字转换成为%xx%xx的格式if(req.getHeader("User-Agent").contains("Firefox")){// 如果是火狐浏览器使用Base64编码resp.setHeader("Content-Disposition","attachment; filename==?UTF-8?B?"+newBASE64Encoder().encode("中国.jpg".getBytes("UTF-8"))+"?=");}else{// 如果不是火狐,是IE或谷歌,使用URL编码操作resp.setHeader("Content-Disposition","attachment; filename="+URLEncoder.encode("中国.jpg","UTF-8"));}/** * /斜杠被服务器解析表示地址为http://ip:prot/工程名/ 映射 到代码的Web目录 */InputStreamresourceAsStream=servletContext.getResourceAsStream("/file/"+downloadFileName);// 获取响应的输出流OutputStreamoutputStream=resp.getOutputStream();// 3、把下载的文件内容回传给客户端// 读取输入流中全部的数据,复制给输出流,输出给客户端IOUtils.copy(resourceAsStream,outputStream);}}

218.文件下载-使用URLEncodeer解决谷歌和IE浏览器中午下载名乱码问题

如果客户端浏览器是 IE 浏览器 或者 是谷歌浏览器。我们需要使用URLEncoder 类先对中文名进行 UTF-8 的编码操作。
因为 IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。

// 把中文名进行 UTF-8 编码操作。Stringstr="attachment; fileName="+URLEncoder.encode("中文.jpg","UTF-8");// 然后把编码后的字符串设置到响应头中response.setHeader("Content-Disposition",str);

219.文件下载-Base64编解码操作

如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行 BASE64 的编码操作。

这时候需要把请求头 Content-Disposition: attachment; filename=中文名
编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=

=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明。
=?
charset B
xxxx
?=

220.文件下载-使用Base64编解码解决火狐浏览器附件中文乱码问题

因为火狐使用的是 BASE64 的编解码方式还原响应中的汉字。所以需要使用 BASE64Encoder 类进行编码操作。

// 使用下面的格式进行 BASE64 编码后Stringstr="attachment; fileName="+"=?utf-8?B?"+newBASE64Encoder().encode("中文.jpg".getBytes("utf-8"))+"?=";// 设置到响应头中response.setHeader("Content-Disposition",str);

221.文件下载-使用User-Agent请求头判断,动态切换不同的方案解决所有浏览器附件中文乱码问题

那么我们如何解决上面两种不同编解码方式呢。我们只需要通过判断请求头中 User-Agent 这个请求头携带过来的浏览器信息即可判断出是什么浏览器。

Stringua=request.getHeader("User-Agent");// 判断是否是火狐浏览器if(ua.contains("Firefox")){// 使用下面的格式进行 BASE64 编码后Stringstr="attachment; fileName="+"=?utf-8?B?"+newBASE64Encoder().encode("中文.jpg".getBytes("utf-8"))+"?=";// 设置到响应头中response.setHeader("Content-Disposition",str);}else{// 把中文名进行 UTF-8 编码操作。Stringstr="attachment; fileName="+URLEncoder.encode("中文.jpg","UTF-8");// 然后把编码后的字符串设置到响应头中response.setHeader("Content-Disposition",str);}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 1:15:43

Gin + Vue3 + ElementPlus 后台:固定窗口限流的实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:15:28

Gin + Vue3 + ElementPlus 后台前端:Vue3 + TypeScript + Vite 实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:15:12

Gin + Vue3 + ElementPlus 的接口自动注册:EasyGoAdmin 实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:14:54

python selenium启动常用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:14:53

android的两种启动service方式及混合方式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 1:14:53

研究生基础知识

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华