首页 > 编程语言 >JavaWeb实现简单的文件上传和下载

JavaWeb实现简单的文件上传和下载

时间:2023-11-27 13:55:21浏览次数:43  
标签:文件 单项 JavaWeb resp filename 浏览器 上传 下载

一、文件上传
1.1 文件上传的简单实现
前端的标签介绍
① 需要一个<form>表单标签,请求方式为post请求

PS:因为get请求时url有长度限制,而带有文件上传的url一般会超出get请求的长度限制,所以只能用post

② <form>标签中需添加enctype属性,属性值必须为multipart/form-data

enctype属性:encodetype的缩写,就是编码类型的意思
multipart/form-data属性值:multipart是多元的意思,表示数据由多段的形式拼接而成,既有文本又有文件,每一个表单项表示一段数据,拼接后以二进制流的方式提交到服务器
PS:enctype属性值只有设置为multipart/form-data时,才能实现文件的上传

③ <form>标签里面添加<input type=file>标签,在此处添加需要上传的文件
④ 后台服务器中使用Servlet 接受和处理上传的数据

实现代码

前端页面

<%-- 添加表单项 --%>
<form action="" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"> <br>
头像:<input type="file" name="profile"> <br>
<input type="submit" value="上传">
</form>

后台Servlet

@WebServlet("/fileUpload")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传成功!");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}

1.2 http协议内容分析
分析图

 

分析请求头
Content-Type参数:对应表单中的enctype属性,表示提交数据的编码类型
Content-Type的两个参数值
multipart/form-data:对应enctype属性值,就是表示提交的数据以多段形式拼接而成,并以二进制流形式发给服务器
boundary=分隔符:表示每段数据的分隔符,由浏览器每次随机生成,这里演示的分隔符为----WebKitFormBoundarynn2YwuHk30PBRDRM(前面一般带有4个减号)
分析请求体
----WebKitFormBoundarynn2YwuHk30PBRDRM:分隔符,表示一段数据(一个表单项)的开始
----WebKitFormBoundarynn2YwuHk30PBRDRM--:结束分隔符,在原来的分隔符后再添加2个减号,表示整段数据的结束,即在最后一个表单项后面出现
表单项内容分析
Content-Disposition:对当前表单项的描述,参数值有form-data、name="表单名"
Content-Type:表示上传的文件MIME类型,值为image/jpeg
空行:在表单项描述和表单项值之间会有空行隔开,类似于请求头与请求体之间的请求空行
表单项的值:如果是文本类型的表单项,就直接显示表单项输入框中的值,如果是文件类型,就是一些文件数据,一般文件的数据都会很多,所以浏览器会隐藏起来,但可以在后台打印查看
1.3 解析文件上传时的数据
使用第三方(apache)jar包:commons-fileupload-…jar、commons-io-…jar

jar包中重要类及其方法介绍

ServeltFileUpload类:用于解析文件上传时的数据
boolean isMultipartContent(req):ServletFileUpload类中的静态方法,判断上传的表单数据是否是多段的格式
List<FileItem> parseRequest(req):解析上传的数据(多段数据格式的前提下)
FileItem类:对应一个表单项
boolean isFormField():判断当前的表单项是否是普通类型的表单项,true表示普通表单项,如文本,false表示非普通表单项,如文件
String getFieldName():获取表单项的名称,即获取<form>标签的name属性值
String getString("utf-8"):获取普通表单项的值,加上参数"utf-8",设置编码,可以防止中文乱码
String getName():获取上传的文件名,表单项是文件类型才能使用
write(文件路径):将上传的文件写到对应的硬盘地址中
jar包方法在Servlet中的使用
① 判断当前表单数据是否多段格式,如果不是就不能进行文件数据解析,下列操作都是在多段格式数据下进行
② 先创建一个表单项工厂类对象FileItemFactory
③ 根据创建的表单项工厂类对象创建出ServletFileUpload对象,用于解析数据
④ 调用servletFileUpload的解析方法,对表单数据进行解析,获取数据段集合,即表单项集合List<FileItem>(会有异常,需要捕获)
⑤ 遍历获取的表达项集合,判断每一个表单项类型,如果是普通类型表单项就只打印表单项名和表单项值,如果是文件类型表单项,就打印表单项名和文件名,同时将上传的文件写到指定位置

@WebServlet("/fileUpload")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传成功!");

// 1. 判断当前表单是否是多段格式
if (ServletFileUpload.isMultipartContent(req)) {
// 2. 创建表单项工厂对象
FileItemFactory fileItemFactory = new DiskFileItemFactory();

// 3. 根据工厂对象创建出ServletFileUpload对象
ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);

try {
// 4. 调用方法解析数据,获取表单项集合
List<FileItem> fileItems = fileUpload.parseRequest(req);

// 5. 遍历表单项集合,对不同类型的表单项做不同处理
for (FileItem item : fileItems) {
// 5.1 判断每一个表单项的类型,是否是普通类型
if (item.isFormField()) {
// 普通类型表单项
System.out.println("表单项名:" + item.getFieldName());
System.out.println("表单项值:" + item.getString("utf-8"));
} else {
// 不是普通类型表单项
System.out.println("文件表单项名:" + item.getFieldName());
System.out.println("文件名:" + item.getName());

// 将上传的文件写到指定位置,文件名不变
item.write(new File(
"D:\\ideaProject\\workplace_java\\Pro_FileUpAndDown\\src\\main\\webapp\\" +
item.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}


二、文件下载
参考博文

java web文件下载功能实现
web项目中各种路径的获取
2.1 文件下载的简单实现
2.1.1 通过超链接下载
创建download_href.jsp(或html),创建两个超链接,分别对应压缩包资源和图片资源

PS:两个文件都在工程的webapp/file目录下,这里的href的值是绝对路径,客户端的绝对路径都是相对于服务器根目录(http://ip地址:端口号),而不是工程中的webapp目录,所以需要加上工程路径(虚拟目录),推荐使用<%= request.getContextPath()%>/动态获取工程路径

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>通过超链接下载文件</title>
</head>
<body>
<%-- 创建两个超链接 --%>
<a href="<%= request.getContextPath()%>/file/like.rar">压缩包下载</a> <br/>
<a href="<%= request.getContextPath()%>/file/doge.png">图片下载</a>
</body>
</html>

在浏览器中直接点击对应链接,下载对应文件

PS:一般这种方法实现文件下载是不可行的,因为很多浏览器可以识别该文件格式,识别后就会直接打开对应的超链接(打开后一般是错误页面)。只有浏览器不能识别该文件格式的时候,才能实现下载

2.1.2 跳转到后台Servlet实现下载
创建download.jsp页面,通过点击对应超链接,跳转到FileDownloadServlet(资源路径为/fileDownload)中,并带上请求参数,将文件名传给后台

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<%-- 在后台通过Servlet下载文件 --%>
<a href="<%= request.getContextPath()%>/fileDownload?filename=like.rar">压缩包下载</a> <br/>
<a href="<%= request.getContextPath()%>/fileDownload?filename=doge.png">图片下载</a>
</body>
</html>

在FileDownloadServlet中,先获取请求参数中的文件名,再通过ServletContext对象获取文件名对应的MIME类型,然后将对应的MIME类型设置为响应时的数据类型,再然后通过设置响应头Content-Disposition为attachment,告知浏览器客户端对数据的进行下载的操作,最后获取文件的位置,并通过输入流和输入流将文件写到浏览器端(使用jar包中的工具类实现更方便)

PS1:如果不设置响应头,在浏览器端就不能实现下载的功能,只会将图片展示到页面

PS2:服务器端的绝对路径指的是相对于工程的webapp目录,即http://ip地址:端口号/工程路径,所以在获取文件的位置时只需写成/file/文件名;另一种解释为相对路径/file/文件名中的第一个/被服务器解析成http://ip地址:端口号/工程路径,并映射到工程的webapp目录下

PS3:在获取文件名时,有两种方法

通过servletContext对象直接调用InputStream getResourceAsStream(文件位置),可以直接将文件位置转成输入流
先通过servletContext对象调用String getRealPath(文件位置),获取文件的真实路径,再通过FileInputStream(文件真实路径),获取输入流
@WebServlet("/fileDownload")
public class FileDownloadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取请求参数中的文件名
String filename = req.getParameter("filename");
System.out.println(filename);

// 2. 获取ServletContext对象,用于获取文件对应的MIME类型
ServletContext servletContext = getServletContext();

// 3. 获取文件名对应的MIME类型,这里直接写filename也可
String mimeType = servletContext.getMimeType("/file/" + filename);
System.out.println(mimeType);

// 4. 将对应的MIME类型设置为响应时的数据类型
resp.setContentType(mimeType);

// 5. 设置响应头,告知浏览器要对文件做下载的操作,这里文件名可以自定义,不一定与原文件一样
resp.setHeader("Content-Disposition", "attachment;filename=" + filename);

// 6. 获取文件的路径,并转成输入流
InputStream inOfFile = servletContext.getResourceAsStream("/file/" + filename);

// 7. 获取响应的输出流
ServletOutputStream outOfFile = resp.getOutputStream();

// 8. 通过IO工具类,直接将输入流复制到输出流,然后写到浏览器客服端(简单方便)
IOUtils.copy(inOfFile, outOfFile);
}

@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}


遇到的小问题:第一次运行,在浏览器点击超链接后,显示“无法访问此网站”,而且服务器收到多次请求。通过debug模式检查后,发现每次获取的文件输入流inOfFile值都为null,而且target文件夹中也没有图片和压缩包的资源,原因可能是webapp目录下的资源没有加载成功

解决方法:执行一次maven的clean命令,再重启服务器,就能解决问题

2.2 文件名的中文乱码问题
引入问题:在设置Content-Disposition响应头时,可以设置与原文件不一样的文件名,如果设置为中文名,会出现中文乱码的问题,导致下载到本地的文件名是乱码

2.2.1 使用URL编码
适用浏览器:谷歌、IE

使用:将resp.setHeader("Content-Disposition", "attachment;filename=" + filename),中的filename换成URLEncoder.encode("带中文的文件名", "utf-8");

resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("狗头.png", "utf-8"));

2.2.2 使用BASE64编码
适用浏览器:火狐

BASE64的简单测试

PS:BASE64编码类BASE64Encoder中的编码方法encode()不是静态方法,所以需要先创建出编码对象,才能调用;BASE64解码类BASE64Decoder也是一样

测试代码
@Test
public void testBASE64() throws IOException {
// 1. 定义一个带中文的字符串
String content = "我爱中国!";

// 2. 创建Base64编码对象
BASE64Encoder encoder = new BASE64Encoder();

// 3. 先将内容字符串转成字节
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);

// 4. 调用编码方法对字节进行编码,获取编码后的字符串
String encodeString = encoder.encode(bytes);
System.out.println("编码结果:" + encodeString);

// 5. 创建Base64解码对象
BASE64Decoder decoder = new BASE64Decoder();

// 6. 将编码的字符再转成字节数组
byte[] decodeBuffer = decoder.decodeBuffer(encodeString);

// 7. 获取解码后的字符串
String decodeString = new String(decodeBuffer, StandardCharsets.UTF_8);
System.out.println("解码结果:" + decodeString);
}


测试结果

 

BASE64在文件下载中的使用:将resp.setHeader("Content-Disposition", "attachment;filename=" + filename),中的filename换成=?charset?B?XXXX?=

=?charset?B?XXXX?=的解释:

=?:表示编码内容的开始
charset:字符集,如utf-8
B:表示BASE64编码,不用修改
XXXX:表示通过BASE64编码之后的内容
?=:表示编码内容的结束
// 先获取BASE64编码后的内容
String encodeString = new BASE64Encoder.encode("狗头.png".getBytes(StandardCharsets.UTF_8));

// 再设置响应头
resp.setHeader("Content-Disposition", "attachment;filename==?utf-8?B?" + encodeString + "?=");

2.2.3 通过User-Agent请求头选择使用对应编码方式
PS:因为不同浏览器适用不用的编码方式,为统一解决所有浏览器的文件名中文乱码问题,可以通过User-Agent请求头来判断浏览器的版本,从而使用对应的编码方式

// 5. 设置响应头,告知浏览器要对文件做下载的操作
// 获取User-Agent请求头,判断浏览器种类
String reqHeader = req.getHeader("User-Agent");
// 判断请求头中是否包含火狐的字样
if (reqHeader.contains("Firefox")) {
// 如果是火狐浏览器,就使用BASE64编码
// 先使用BASE64对内容进行编码,再插入
String encodeString = new BASE64Encoder().encode("狗头.png".getBytes(StandardCharsets.UTF_8));
resp.setHeader("Content-Disposition", "attachment;filename==?utf-8?B?" + encodeString + "?=");
} else {
// 如果是非火狐浏览器,如IE、谷歌,就使用URL编码即可
resp.setHeader("Content-Disposition", "attachment;filename=" +
URLEncoder.encode("狗头.png", "utf-8"));
}

 

参考文章:http://blog.ncmem.com/wordpress/2023/11/27/javaweb%e5%ae%9e%e7%8e%b0%e7%ae%80%e5%8d%95%e7%9a%84%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0%e5%92%8c%e4%b8%8b%e8%bd%bd/

欢迎入群一起讨论

 

 

标签:文件,单项,JavaWeb,resp,filename,浏览器,上传,下载
From: https://www.cnblogs.com/songsu/p/17859084.html

相关文章

  • 活动回顾|阿里云云原生 Serverless 技术实践营 深圳站回放&PPT下载
    11月24日“阿里云云原生Serverless技术实践营”深圳站圆满落幕。活动受众以关注 Serverless技术的开发者、企业决策人、云原生领域创业者为主,活动形式为演讲、动手实操,让开发者通过一个下午的时间增进对Serverless技术的理解,快速上手Serverless,拥抱云计算新范式带来的技......
  • JavaWeb实现文件上传下载功能实例解析
    在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现。对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择......
  • npm下载node-sass包安装失败,需要下python2?
    问题这个问题真的老问题了,今天在跑一个老项目的时候又遇到了。每次遇到都会感慨,这个包真的是是非多啊,解决方案也很简单,不用下python环境,单纯是版本问题查了下网上有挺好的一篇博客:http://www.inspinia.net/a/388314.html?action=onClick里面的报错跟我的一模一样:解决方案......
  • javaWeb 文件上传及下载
    前言网络上最常用的就是文件的上传和下载,本文讲解基于org.apache.commons.fileupload包及org.apache.commons.io包实现文件上传;通过设置响应头属性并基于文件流方式实现文件下载。一、文件上传1、实现思路文件上传是通过表单上传,因此首先需要判断表单中是否包含文件类型的标签,如......
  • 【漏洞复现】蓝凌OA sysUiComponent 任意文件上传漏洞
    阅读须知此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。本文所提供的工具仅用于学习......
  • Linux-源码下载地址
    【https://mirrors.edge.kernel.org/pub/linux/kernel/v2.4/】【https://www.kernel.org/】......
  • XcodesApp快速下载切换指定版本的xcode
    XcodesApp地址:https://github.com/RobotsAndPencils/XcodesApp⭐️:3.6k语言:Swift作为一名Apple开发者,你的macOS上是否经常会安装多个版本的Xcode呢?特别是当Xcode新的测试版本出来后。然后是否需要经常在多个Xcode版本之间切换呢?这些推荐一个开源工具,是目前安装和在......
  • 在Megatron-Deepspeed项目中如何下载和预处理Wikipedia数据集
    更详细的查看https://github.com/marsggbo/Megatron-DeepSpeed/blob/main/tutorials/gpt2_wikipedia.md下载Wikipedia压缩数据集(enwiki-latest-pages-articles.xml.bz2)再使用wikiextractor工具将数据集解压缩pipinstallwikiextractorpython-mwikiextractor.WikiExt......
  • node版本管理,勇士nvm下载安装使用
    node不同版本管理比较麻烦,推荐一个工具nvm进行版本管理 下载地址:nvm-windows  ......
  • JavaWeb实现文件的上传和下载
    文件的上传和下载,是非常常见的功能,很多系统,或者软件中经常使用文件的上传和下载.比如QQ头像/邮箱中的上传和下载功能/OA系统中审批中附件的上传.1.文件的上传步骤:有一个form标签,method=post请求form标签的encType属性的值必须为multipart/form-data值在form标签中,使......