Java 文件上传与下载
MultipartFile这个类一般是用来接受前台传过来的文件Part能获取所有的请求参数的参数名,而Parameter只能获取非文件类型的参数名Part不能获得普通参数的参数值,只能从 getParameter(String) 获取参数值想要上传文件到服务器,必须使用Part获得二进制的输入流Part能获得上传文件的文件大小、文件类型HttpServletRequest request@Requ
文章目录
Ⅰ、文件上传
一、form-data 类型
form-data 类型即常用的表单提交
两种处理参数的方式
- MultipartFile 类接受前台传过来的文件
- part 接收字节流
@RequestPart 作用类似 @RequestParam
1、postMan 请求
2、文件上传接口
直接上代码
@RestController
public class TestFile {
private BufferedOutputStream bufferedOutputStream = null;
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public String readFile(HttpServletRequest request, @RequestParam("name") String name, @RequestPart("file1") MultipartFile file3,@RequestPart("photo") MultipartFile photo) throws IOException, ServletException {
String path= "I:\\spring\\spring-mybatis-plus\\src\\main\\resources\\public\\static\\";
System.out.println(name);
/*
第一种 : 使用 MultipartFile 封装好的 transferTo() 方法保存文件
photo.transferTo(new File(path+photo.getOriginalFilename()));
第二种 : 使用 MultipartFile 字节流保存文件
fileUtil(file3, String.valueOf(path));
第三种 : 使用 Part 接收文件字节流
Part file2 = request.getPart("file2");
file2.write(path + file2.getSubmittedFileName());
*/
// request.getParts() 获取的是全部参数(name,age,file1,file2),包括文件参数和非文件参数
for (Part part : request.getParts()) {
// 获取文件类型
part.getContentType();
// 获取文件大小
part.getSize();
// 获取文件名
part.getSubmittedFileName();
// 获取参数名 (name,age,file1,file2)
part.getName()
if(part.getContentType()!=null){
part.write(path + part.getSubmittedFileName());
}else{
// 可以获取文本参数值,文本参数 part.getContentType() 为 null
request.getParameter(part.getName())
}
}
return "success";
}
public String fileUtil(MultipartFile file, String path) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(
new File(path + file.getOriginalFilename())));
bufferedOutputStream.write(bytes);
bufferedOutputStream.close();
return file.getOriginalFilename() + "success upload";
} catch (Exception e) {
return file.getOriginalFilename() + "failed to upload ---> " + e;
}
} else {
return file.getOriginalFilename() + "You failed to upload file was empty.";
}
}
}
实际开发中,上面有很多需要优化的地方,比如写出文件工具类,获取项目路径(注意测试和打包上线路径),三种方法使用一种即可,流参数输出之后,就获取不到,不像其他参数可以在该请求的任意位置获取。
如果一个输入框多个文件,可以使用
List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("");
// 也可以使用 part.getName() 获取参数名做判断
part.getName()
或者 MultipartFile[] file3
public String readFile(HttpServletRequest request, @RequestParam("name") String name, @RequestPart("file1") MultipartFile[] file3)
3、 测试
二、binary 类型
binary 这一类型,指的就是一些二进制文件类型,如application/pdf,指定了特定二进制文件的MIME类型。就像对于text文件类型若没有特定的子类型(subtype),就使用 text/plain。类似的,二进制文件没有特定或已知的 subtype,即使用 application/octet-stream,这是应用程序文件的默认值。
对于application/octet-stream,只能提交二进制,而且只能提交一个二进制,如果提交文件的话,只能提交一个文件,后台接收参数只能有一个,而且只能是流(或者字节数组)。
1、postMan 请求
2、文件上传接口
就是简单的文件读写,其中细节可以根据需求自行添加
@RequestMapping(value = "/upload2",method = RequestMethod.POST)
public String upload2(HttpServletRequest request) throws IOException {
ServletInputStream inputStream = null;
FileOutputStream fileOutputStream = null;
try {
inputStream = request.getInputStream();
fileOutputStream = new FileOutputStream(new File("I:\\spring\\spring-mybatis-plus\\src\\main\\resources\\public\\static\\微信图片_20210729130156.jpg"));
int len;
byte[] bytes = new byte[1024];
while((len = inputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
return "上传失败";
}
finally {
if(fileOutputStream!=null){
fileOutputStream.close();
}
if(inputStream!=null){
inputStream.close();
}
}
return "上传成功";
}
3、测试
上传成功
三、springboot 配置
如果不配置,可能上传文件过大会报错,默认上传文件小于 1MB
如果是 springboot 项目,可以通过配置文件限制文件上传大小
文件上传配置类 MultipartAutoConfiguration
MultipartProperties 默认参数,可以看到默认开启,单个文件最大 1MB,单个请求最大 10MB
application.yml 上传文件配置
spring:
servlet:
multipart:
max-file-size: 10MB # 单个文件最大 10MB
maxRequestSize: 100MB # 单个请求最大 100 MB
四、总结
part 使用起来比较方便,接受的就是字节流,读取文件类型,文件名,文件大小也比较方便,不清楚 getParameter() 和 getPart() 区别 的可以看我另一篇博客
https://blog.csdn.net/qq_41538097/article/details/117637372
Ⅱ、文件下载
一、下载本地资源
Content-Disposition 详细说明:参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
response.setHeader("Content-Disposition", "attachment;fileName=test.txt" ;
意味着消息体应该被下载到本地;大多数浏览器会呈现一个“保存为”的对话框,将 filename 的值预填为下载后的文件名,假如它存在的话(fileName 或者 filename 都可以使用)
response.setHeader(“Content-Disposition”, “inline; filename=test.jpg” );
设置为在线打开
public class FileDownload {
@RequestMapping("/download")
public void download(String fileName, HttpServletResponse response,boolean isOnLine) throws IOException {
// 路径可以指定当前项目相对路径
File file = new File("C:\\Users\\Administrator\\Pictures\\小程序\\" + fileName);
if (file.exists()) {
FileInputStream fileInputStream = new FileInputStream(file);
ServletOutputStream outputStream = response.getOutputStream();
if(!isOnLine){
response.setContentType("application/octet-stream");
// 如果文件名为中文需要设置编码
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("test.jpg", "utf8"));
// 返回前端文件名需要添加
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
}
byte[] bytes = new byte[1024];
int len;
while ((len = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
}
}
}
测试下载:
接口:http://localhost:8081/download?fileName=test.jpg
不同的浏览器弹出的操作可能不同
chrome浏览器
IE浏览器
都可以下载成功
测试在线打开
http://localhost:8081/download?fileName=test.jpg&isOnLine=false
如果不指定下载,则默认是在线打开(谷歌浏览器)或者使用 response.setHeader(“Content-Disposition”, “inline; filename=test.jpg” );设置为在线打开
二、下载网络资源
到网易云音乐找个歌曲
@RequestMapping("/downLoadMusic")
public void downloadNetworkFile(HttpServletResponse response) throws IOException {
URL url = new URL("https://m701.music.126.net/20210808175335/c2ed0b504dd7bf3f86ac67a40fd092d2/jdyyaac/565b/065f/0358/a1cd0e25a815dffcc0c1422398efde9e.m4a");
URLConnection urlConnection = url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
ServletOutputStream outputStream = response.getOutputStream();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("起风了.m4a", "utf8"));
// 返回前端文件名需要添加
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
outputStream.close();
inputStream.close();
}
chrome测试
IE测试
下载成功
注意:下面的情况针对静态资源在 resources 目录下,运行找不到资源的原因以及解决办法
首先,开发环境使用绝对路径肯定是不会错的,但部署运行环境就需要考虑路径问题,下面解决这个问题
对于 springboot 项目打成 jar 包,在 window/linux 系统使用 java -jar 运行时,需要考虑路径问题,因为是运行未解压 jar 包,如果直接指定 resources 目录下文件也运行是找不到的,此时必须使用输入流才可以 InputStream resourceAsStream = FileDownload.class.getClassLoader().getResourceAsStream("static/0.jpg"); 亲测对于 window/Linux 都有效
更多文件下载可参考 https://www.jb51.net/article/203861.htm
2022-11-02 更新,下面的前端示例没有使用上面的后端接口,但都大同小异,下面用到的后端接口可以在如下链接找到
- 上传:https://yixiu.blog.csdn.net/article/details/127564203
- 下载:https://yixiu.blog.csdn.net/article/details/127564195
Ⅲ、前端简单示例
下面以上传和下载 excel 为例
一、上传 excel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>导入文件</title>
</head>
<body>
<p>使用 jQuery ajax 发送 post 请求方式导入 excel 文件</p>
<form id="form1" name="form1" enctype="multipart/form-data">
<input type="file" id="file" name="file"/> <br/>
<input type="text" id="flag" name="flag"/> <br/>
<input type="text" id="password" name="password"/> <br/>
<input type="button" value="提交" onclick="importExcel()">
</form>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<script type="text/javascript">
function importExcel() {
var formData = new FormData(); //需要用到formData
formData.append('file', $("#file")[0].files[0]); //添加选择的文件 key值为file
formData.append('flag', $("#flag")[0].value)
formData.append('password', $("#password")[0].value)
console.log(formData)
$.ajax({
type: "post",
url: "http://127.0.0.1:8080/student/import",
// 自定义 header
headers: {
TOKEN: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsInVzZXJOYW1lIjoidGVzdDMiLCJleHAiOjE2NjcyOTc2ODR9.4SzdXhzX59agxYXnk7IW6Q-iXX9VXlNDnYd3gW-uH8fpxNt32oo7TpRZlRFRCIXgwVkG5MI4UpCQT-NexZO4bw"
},
data: formData, //提交的数据,
processData: false, // processData 默认为 true,jQuery 特有,默认将发送的数据序列化 application/x-www-form-urlencoded 类型
contentType: false,// 默认类型 application/x-www-form-urlencoded ,为了避免 JQuery 对其操作,从而失去分界符,使服务器不能正常解析文件
error: function (data, status, xhr) {
alert("提交失败")
},
success: function (data, status, xhr) {
console.log(data)
}
})
}
</script>
二、下载 excel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Ajax 请求</title>
</head>
<body>
<p>导出 excel </p>
<button onclick="download()">导出</button>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<script type="text/javascript">
function download() {
$.ajax({
type: "POST",
url: "http://127.0.0.1:8080/student/export",
// 自定义 header
headers:{
TOKEN:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsInVzZXJOYW1lIjoidGVzdDMiLCJleHAiOjE2NjcyOTUwMzJ9.y2JdlHNSYtbzbcVlw6IvO_k4o8UAAkyzzWQKHl6hTfN8MZeTlMvfHR1bCL-xYYgHdr7psj_Lz3HXgLvyv0WnZA"
},
xhrFields: {
responseType: 'blob'
},
success: function(data,status,xhr){
// 获取后端指定的文件名
var contentDisposition=decodeURI(xhr.getResponseHeader("Content-Disposition"))
var fileName = contentDisposition.substring(contentDisposition.indexOf("fileName=") + 9)
console.log(fileName)
const url = window.URL.createObjectURL(new Blob([data]));
const a = document.createElement('a');
a.href = url
a.setAttribute("download", fileName)
a.click()
window.URL.revokeObjectURL(url)
},
error: function (data,status,xhr) {
alert("下载失败")
}
})
}
</script>
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)