JavaWeb文件上传
文件上传总览文件上传主要是指将当地文件(包罗但不限于图片、视频、音频等)上传到服务器,提供其他用户浏览或下载的过程。在一样平常生活中,我们在很多情况下都需要利用文件上传功能,比如:发微博、发朋友圈等(这里主要是指照片和视频文件),可见文件上传的重要性。文件上传功能实现需要涉及到两个部分,一个部分是前端步伐、一个部分是后端步伐,本文将前后端分开解说。
接收文件
文件上传的前端准备
前端若想举行文件上传(网页的文件上传),就必须要有文件上传的三要素,前端必须要有这三要素才可以完成文件上传。前端要想实现文件上传就必须定义一个form表单,其中必须要有一个表单项范例为file;表单的提交方式必须是post方式,其原因是get方式提交有大小限定(并且不是很大),但文件上传大概涉及到大文件,以是说应该利用post方式提交表单;其末了一个要素,也是最重要的要素,就是在form表单中需要通过enctype属性设置表单的编码格式为 "multipart/form-data",这个设置非常重要——文件的本质上是二进制数据,而form表单的默认编码格式(x-www-form-urlencoded)是不适合通报二进制数据的。
假如说利用form表单默认的编码格式传输文件的问题:简单概括就是利用x-www-form-urlencoded编码格式传输文件,不会传输文件中的内容(数据),只能传输一个文件名。假如说我有一个.txt文本文件,利用默认的编码格式举行上传:
(该文件是有内容的)
https://i-blog.csdnimg.cn/direct/a197229202664e4d9eccee0b84865bcf.png
该表单的编码格式是默认的x-www-form-urlencoded
https://i-blog.csdnimg.cn/direct/4e061661fb6d45a7acced57d1bbc6b23.png https://i-blog.csdnimg.cn/direct/36a86fcbf4994582869aed6de1d7aee6.png
发现上传的文件数据只有上传的文件的文件名,这证实了若利用form表单的默认编码格式,那么传输文件是失败的,只会上传文件的文件名。 总结一下文件上传的前端准备: 需要一个form表单,表单中需要一个表单项范例为file,并且该表单的提交方式必须为post,而且表单的编码格式必须修改为multipart/form-data。
文件上传的后端步伐
文件上传的后端步伐起首肯定是需要一个控制方法(SpringBoot),用于接收文件上传这个“功能接口”的请求,并且请求的其他数据之前怎么接收现在仍旧怎么接收(比如简单参数、实体参数、日期参数等,还是像之前那么接收即可);请求提交的文件需要用SpringBoot提供的对象MultipartFile接收(前端中file表单项的名称必须和控制方法参数列表中MultipartyFile对象名完全雷同)
创建请求处置处罚类,并定义文件上传功能接口的控制器方法
@Slf4j
@RestController
public class FileController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) {
log.info("文件上传:{},{},{}", username, age, image);
return Result.success();
}
}
此时通过前端页面发起请求:
<!DOCTYPE html>
<html lang="ch-ZN">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
姓名: <input type="text" name="username"><br>
年龄: <input type="text" name="age"><br>
头像: <input type="file" name="image"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
通过调试启动服务,发现此时可以乐成接收到上传的文件,并获取文件的路径:
https://i-blog.csdnimg.cn/direct/1fdbeb5d48a64291a86a32f54a4790fc.png
转到该路径下发现了三个.temp的暂时文件:
https://i-blog.csdnimg.cn/direct/6b04cb3aacef4a22b5d97f2cc7bebff8.png
这是由于该表单有三个表单项,分别是两个text表单项(username和age)和一个file表单项(image文件),两个大小小的就是传的username和age的值,30kb那个temp文件就是上传的文件了,通过记事本打开验证一下:
https://i-blog.csdnimg.cn/direct/bdc4fe4073c84bdc9b0da0a69538a57f.png
https://i-blog.csdnimg.cn/direct/a098f14afab6437c845b6b524dd76deb.png
现在已经在服务端接收到了文件,但是一但步伐运行克制,这些暂时文件也会消散——只要这次请求响应竣事竣事后(不管乐成与否),暂时文件都会被删除,以是说后端步伐不但要接收上传的文件,更重要的是还需要将接收到的文件生存起来。总结一下文件上传的准备工作: 前端页面需要三要素:1.需要在form表单中定义一个表单项的type为file;2.需要指定表单的提交方式为post;3.需要指定表单的编码格式enctype为multipart/form-data。 这三要素缺一不可!后端需要利用Spring框架提供的MultipartFile类来接收前端上传的文件。
当地存储
上述代码乐成在服务端接收到了客户端上传的文件,但是发现只能生成一个暂时文件,一但这次请求响应竣事后,.temp暂时文件就会自动删除,这显然是不符合开发逻辑的,以是说我们需要将这些.temp暂时文件存储起来。此处先先容当地存储。
当地存储就是:在服务端接收到客户端上传的文件之后,将文件存储在当地服务器磁盘中。由于是基于SpringBoot框架,这其实并不困难,由于Spring框架中的MultipartFile对象提供了这样的方法——transferTo,transferTo的参数中需要通报一个File文件对象,就是将.temp暂时文件写入到这个通报的文件对象中完成文件的当地存储。
https://i-blog.csdnimg.cn/direct/0d07d3b45ddb4680a0cfc0683b1a8583.png
此处在获取File对象的时候出现一个问题:不能把文件名写死了——由于假如有多个文件时,若把文件名写死了,那么后接收到的文件就会把前一次接收到的文件的内容覆盖了,整个功能接口就只能生存一个文件了。此时的解决方法是将File对象的文件名存储为上传文件的原文件名,MultpartFile对象中封装了上传文件的所有信息,并且提供了对应的方法获取这些信息,以是说直接可以通过操作MultipartFile对象得到原文件名:
@Slf4j
@RestController
public class FileController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws IOException {
String originalFilename = image.getOriginalFilename();
image.transferTo(new File("E:\imagesTest\" + originalFilename));
return Result.success();
}
}
此时代码就可以正确的接收到客户端上传的文件并将其生存在File指定的路径了:
https://i-blog.csdnimg.cn/direct/6e05a12cc28245099d08795596072004.png
但此时还有一个问题,假如说A用户上传了一个1.txt文件,接着B用户又上传了一个txt文件,也叫1.txt,那么就会出现B用户的文件内容将A用户的文件内容覆盖的问题,由于当地存储是按照文件的原名称存储的,遇到重名文件就会出现这样的问题。
避免文件名重复导致的覆盖问题
大概有人会想到利用System.currentTimeMillis()这个方法获取当前时间的毫秒值作为文件名避免重复,但是在一些大的项目中,有大概同一毫秒就正好有两个人上传了同一个同名的文件,这还是不能从根本上解决文件名重复的问题;解决这个问题可以利用UUID(通用唯一识别码)作为文件名举行存储,UUID是不会重复的(小概率事件不发生)。
但是又存在一个问题,假如说将UUID作为文件名,那么文件的扩展名是什么? 其实也非常简单,由于我们已经得到了文件的原名称,通过探求到原文件名(字符串)中末了一个“.”所处的位置(由于无论文件名是什么,末了一个.之后的才是扩展名),找到末了一个“.”所处的位置之后,直接通过subString()方法截取到末了就可以获取文件的扩展名了,然后再将其和UUID举行拼接,就可以生成一个以UUID定名存储的文件了。
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws IOException {
// 获取原文件名
String originalFilename = image.getOriginalFilename();
// 获取最后一个“.”的索引
int index = originalFilename.lastIndexOf(".");
// 通过subString方法和最后一个“.”索引进行截取,获取文件扩展名
String externalName = originalFilename.substring(index);
// UUID + 扩展名 = 新文件名
String newFileName = UUID.randomUUID().toString() + externalName;
image.transferTo(new File("E:\imagesTest\" + newFileName));
return Result.success();
}
此时,文件在当地存储就是UUID了,再也不会有重复的大概了。
文件上传的大小配置
在SpringBoot框架中,文件上传,默认单个文件允许最大大小为1M。如果需要上传大文件,可以根据需求在application.properties(我利用的是yaml配置文件举行配置,头脑是一样的,只是配置的格式差别)配置文件中举行设置:
servlet:
multipart:
# 配置单个文件最大上传大小(默认1MB)
max-file-size: 10MB
# 配置单个请求最大上传大小(一次请求可以上传多个文件)
max-request-size: 100MB
这样配置之后,就可以根据需求上传大文件了。
当地存储不敷
1.将文件存储到当地,无法通过浏览器等直接访问到文件。
2.若项目中上传了大量文件,这些文件都存储到服务器的当地磁盘,服务器的磁盘空间是非常宝贵的,用来存储用户上传的文件是非常浪费的,并且若服务器的磁盘存储空间满了,想要扩容也是非常困难的,并且现在很多项目都是服务器集群,扩容就更加困难了。
3.若服务器的磁盘损坏,存储的文件恢复极其困难,安全性不高。
现在几乎已经不利用当地存储了,都是利用云存储。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]