首先我们想实现文件上传,我们需要知道什么是文件上传,oss对象存储是什么,接下来带大家一步一步完成文件上传与下载,留一个关注不迷路!!谢谢大家支持
上传,是下载的逆过程。比如抖音上有一个好看的壁纸,你把这个壁纸从抖音里复制到你的手机里就是下载,抖音的头像你觉得不好看,将你自己的照片上传到抖音上,就叫上传。这是最简单的理解方式。
为了增加服务器中知识的数量,一些网站开放了用户提供知识的途径,就是让用户把相关知识的文件以指定的文件格式传输到服务器,这一过程称为“文件上传”,上传后的文件,可以供其他人浏览和下载。说白点,就是客户端将文件传输到服务器的过程叫“文件上传”。
话不多说我们直接上代码!!!
1、文件上传到本地
一、使用form上传
前端
accept: 是用来限制上传的文件格式,比如我们写的这个 image/* 是表示我们只能上传照片,根本看不到除了照片以外的资源
为什么是post请求?
首先:Post传输数据时,不需要在URL中显示出来,而Get方法要在URL中显示。
其次:Post传输的数据量大,可以达到2M,而Get方法由于受到URL长度限制,只能传递大约1024字节.
enctype就是encodetype就是编码类型的意思。
multipart/form-data:是指表单数据有多部分构成,既有文本数据,又有文件等二进制数据的意思。需要注意的是:默认情况下,enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据。
<form action="/upload" id="formData" method="post" enctype="multipart/form-data">
文件上传:<input type="file" name="file" accept="image/*"><br>
<input type="submit" value="上传">
</form>
配置文件
spring:
servlet:
multipart:
location: D:\upload #文件上传的路径
后端
我们一一解释代码是什么意思
我们声明了一个变量path,上面的注解是为了读取我们在核心配置文件中,设置的文件上传的路径, 被读取之后我们的path=D:\upload
这里我们使用post请求安全,因为前端上传的是一个文件,我们需要使用MultipartFile这个类来接收,这个file与前端我们的文件的name是一样的
首先我们先获取了一下上传的文件名字,比如我们上传了一个 1.png照片,我们获取了名字就是一个字符串1.png,然后对他进行字符串截取,从最后一个出现 . 的地方截取,我们需要用到它的后缀名,截取之后获得后缀 suffix 就是我们的 .png ,然后我们创建了一个新的文件名字,他是通过UUID的方式来实现的,然后拼接它的后缀,到现在这个新的文件是没有任何东西的,那我们为什么要创建一个新的文件呢?
因为可能客户端两个人传的照片都是1.png,如果存放在本地之后会被代替掉,所以我们直接新创建一个名字,把他上传的文件传到新文件里就可以了
然后我们new File,创建一个文件,他的路径是 D:\upload\(UUID随机生成的数字)xxx.png(这里是拿png举例子),最后我们把用户上传的这个照片写入到我们的新文件中
注意: 我们不需要自己在D盘创建一个upload文件夹,你这里指定之后,他会自己创建文件夹
@RestController
@RequestMapping("/upload")
public class uploadController {
@Value("${spring.servlet.multipart.location}")
private String path;
@PostMapping
public String upload(MultipartFile file) {
String oldfilename = file.getOriginalFilename();
int i = oldfilename.lastIndexOf(".");
String suffix = oldfilename.substring(i);
String newfilename= String.valueOf(UUID.randomUUID())+suffix;
File file1 = new File(path,newfilename);
try {
file.transferTo(file1);
} catch (IOException e) {
throw new RuntimeException(e);
}
return "上传成功";
}
}
实现
二、使用ajax上传
前端
如果不用ajax,而是使用原生form提交,将form表单的enctype设为"multipart/form-data"也能正常提交到服务器
用ajax上传文件,后台报错the request was rejected because no multipart,研究发现是ajax里contentType设置有问题,不能直接设置为multipart/form-data
,而需要设为false
processData:
默认为true,默认情况下,发送的数据将被转换为对象,设为false不希望进行转换
<!--文件上传一定要是post方式,保证安全, enctype="multipart/form-data"是用来支持文件上传的-->
<form action="/upload" id="formData" method="post" enctype="multipart/form-data">
<!--accept="image/*"只显示图片文件-->
文件上传:<input type="file" name="file" accept="image/*"><br>
<input type="button" onclick="upload()" value="上传">
</form>
<script src="/jquery-3.5.1.min.js"></script>
<script>
function upload() {
var formData = new FormData($('#formData')[0]);
$.ajax({
url:"/upload",
data:formData,
/**
*必须false才会自动加上正确的Content-Type
*/
contentType: false,
/**
* 默认为true,默认情况下,发送的数据将被转换为对象,设为false不希望进行转换
*/
processData: false,
type:"post",
success:function (data) {
console.log(data)
}
})
}
</script>
后端不需要做任何改变
2、oss对象存储
什么是oss对象存储?
「OSS」的英文全称是Object Storage Service,翻译成中文就是「对象存储服务」,官方一点解释就是对象存储是一种使用HTTP API存储和检索非结构化数据和元数据对象的工具。
白话文解释就是将系统所要用的文件上传到云硬盘上,该云硬盘提供了文件下载、上传等一列服务,这样的服务以及技术可以统称为OSS,业内提供OSS服务的厂商很多,知名常用且成规模的蓝队云等。
存储空间(Bucket):存储空间是您用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间。
一、创建oss存储
路径: https://oss.console.aliyun.com/bucket,根据我一步一步来
这里我们创建的存储空间(Bucket)是需要记住的,还有我们的Endpoint的这个地址,一会我们需要在springBoot中用到这个
这里就是我们存放文件的地方
我们在这里创建一个images目录
在这里我们需要有两个密钥:
当您请求访问OSS时,OSS通过使用访问密钥(AccessKey ID和AccessKey Secret)对称加密的方法来验证某个请求的发送者身份
接下来我们准备好了我们的Bucket(存储空间),Endpoint(OSS对外服务的访问域名),以及两个密钥来验证身份,我们就可以使用我们的oss存储来完成上传
要想使用oss对象存储需要导入依赖
<!--oss存储-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
项目构建(略)
二、使用oss存储完成上传
首先我们先现在核心配置文件中设置上传文件的大小
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
这里我们有一个实现类用来统一
@Data
public class FileUploadResult {
// 文件唯一标识
private String uid;
// 文件名
private String name;
// 状态有:uploading done error removed
private String status;
// 服务端响应内容,如:'{"status": "success"}'
private String response;
}
我们的oss把它封装成一个工具类,所有的代码在这里写
@Component
public class UseOss {
//你自己的oss存储的地址
private final String ENDPOINT="oss-cn-beijing.aliyuncs.com";
//这两个密钥是用于身份认证
private static final String ACCESSKEYID="你自己的accessKeyId";
private static final String ACCESSKEYSECRET="你自己的accessKeyscret";
//存储空间的名字
private static final String BUCKETNAME="xcheng-test";
//路径前缀
private static final String URLPREFIX="http://xcheng-test.oss-cn-beijing.aliyuncs.com/";
//创建一个oss的示例对象
private OSS oss=new OSSClient(ENDPOINT, ACCESSKEYID, ACCESSKEYSECRET);
//创建一个允许上传的格式
private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg",
".jpeg", ".gif", ".png"};
}
接下来就是我们的代码了,我们每一行都有注释,如果还是有什么不会的可以来给我评论或者私信
//文件上传
public FileUploadResult uploadFile(MultipartFile uploadFile) {
//设置一个自变量
boolean isLegal = false;
//上传过来的照片与IMAGE_TYPE做比较,如果上传过来的照片的后缀是IMAGE_TYPE类型就返回true
for (String type : IMAGE_TYPE) {
// // 判断上传文件的原始文件名是否以遍历的我们设置的允许上传的格式结尾,不区分大小写
if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(),
type)) {
isLegal = true;
break;
}
}
//封装Result对象,并且将文件的byte数组放置到result对象中
FileUploadResult fileUploadResult = new FileUploadResult();
//进入判断的逻辑是:该照片不是我们想要允许的格式,直接设置该实体类的状态,返回
if (!isLegal) {
fileUploadResult.setStatus("error");
return fileUploadResult;
}
//如果走到这一步,说明满足所有条件的照片
//获得文件的名字
String fileName = uploadFile.getOriginalFilename();
System.out.println("fileName: "+fileName); //全名加上路径
//照片存储的路径
String filePath = getFilePath(fileName);//文件真实存储路径,并将这个照片的名字进行改变
System.out.println("filePath: "+filePath);//images/2db0e12b-c59e-45ef-b35d-ceb218f4d2c4.png
// 上传到阿里云
try {
//根据Bucket的名字来确定是哪个存储空间,filePath存储在存储空间哪个位置,以流的形式 将uploadFile写入到filePath
oss.putObject(this.BUCKETNAME, filePath, new
ByteArrayInputStream(uploadFile.getBytes()));
} catch (Exception e) {
e.printStackTrace();
//上传失败
fileUploadResult.setStatus("error");
return fileUploadResult;
}
//补充实体类
fileUploadResult.setStatus("done");
fileUploadResult.setResponse("success");
//this.aliyunConfig.getUrlPrefix() + filePath 文件路径需要保持数据库
//this.aliyunConfig.getUrlPrefix()+filePath 访问照片的真实路径
fileUploadResult.setName(this.URLPREFIX + filePath);
fileUploadResult.setUid(String.valueOf(System.currentTimeMillis()));
return fileUploadResult;
}
public String getFilePath(String sourceFileName) {
//获得文件后缀
int index = sourceFileName.lastIndexOf(".");
String suffix = sourceFileName.substring(index);
//这里生成了一个新路径
//在我们存储空间下的 /images/ + 随机数 + 文件后缀
return "images/" + UUID.randomUUID() + suffix;
}
这里我们就完成了文件上传,页面使用的是form完成即可,controller层调用它
如果最后返回的是 success就说明成功了
我们再到阿里云去看一下
三、 使用oss存储遍历所有照片
public List<OSSObjectSummary> list() {
// 设置最大个数,查看多少个最大。
final int maxKeys = 200;
/**
* 列出存储桶中的对象。此方法将请求OSS(Amazon S3兼容的对象存储服务)列出指定存储桶中的对象。
*
* maxKeys 指定返回的最大对象数量。此参数用于控制返回的对象列表长度。
* 返回一个包含存储桶中对象摘要的列表。每个对象摘要包含对象的基本信息,如对象名称、大小等。
*/
ObjectListing objectListing = oss.listObjects(new ListObjectsRequest(this.BUCKETNAME).withMaxKeys(maxKeys));
/**
* 从对象列表中获取对象摘要列表。此方法用于提取对象列表中每个对象的详细摘要信息。
* 返回一个包含存储桶中对象摘要信息的列表。每个摘要包含对象的名称、大小、最后修改日期等信息。
*/
List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
return sums;
}
获取到每个照片的示例将它返回前端
@PostMapping("/queryAll")
public List<OSSObjectSummary> queryAll(MultipartFile uploadFile){
List<OSSObjectSummary> list = useOss.list();
return list;
}
html 页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
</div>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.11.2/jquery.js"></script>
<script>
var pre = 'https://xcheng-test.oss-cn-beijing.aliyuncs.com/';
$(function () {
listfile();
});
//文件列表
function listfile() {
$.ajax({
url: "/upload/queryAll",
type: 'POST',
success: function (res) {
console.log(res);
for (var i = 0; i < res.length; i++) {
$('div').append('<img src="' + pre + res[i].key + '" style="width: 300px;height: 300px; padding: 10px"></img>')
}
}
});
}
</script>
</body>
</html>
f12可以看到,我们照片的实例对象
照片的前缀是我们自己当时在util包下设置的
四、使用oss存储完成下载 以及删除
给遍历的每个照片加上路径,因为他是根据路径来删除的
方法
//文件下载
function downloadfile(src) {
//去掉前面的pre我们指定的路径 https://xuecheng-ce.oss-cn-shanghai.aliyuncs.com/
var fileName = src.split(pre)[1];
window.location.href = "/upload/download?fileName=" + fileName;
}
//文件删除
function deletefile(src) {
//去掉前面的pre我们指定的路径 https://xuecheng-ce.oss-cn-shanghai.aliyuncs.com/
var fileName = src.split(pre)[1];
$.ajax({
url: "/upload/del",
data: {fileName: fileName},
type:"post",
success: function () {
alert('删除成功',fileName);
//删除页面
location.reload();
}
});
}
工具类
//删除照片
public FileUploadResult delete(String objectName) {
//删除objectName这个文件是我们储存文件下的images images/2db0e12b-c59e-45ef-b35d-ceb218f4d2c4.png
//指定是哪一个存储空间的哪个文件
oss.deleteObject(this.BUCKETNAME, objectName);
//实体类
FileUploadResult fileUploadResult = new FileUploadResult();
fileUploadResult.setName(objectName);
fileUploadResult.setStatus("removed");
fileUploadResult.setResponse("success");
return fileUploadResult;
}
//文件下载
public ResponseEntity<byte[]> exportOssFile(String objectName) {
//文件上传objectName: images/2db0e12b-c59e-45ef-b35d-ceb218f4d2c4.png
System.out.println("文件下载objectName: "+objectName);
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
//根据云存储的Bucket的名字找到这个文件,获取到示例,进行读取
OSSObject ossObject = oss.getObject(this.BUCKETNAME, objectName);
// 读取文件内容。
InputStream is = ossObject.getObjectContent();
//下载时会带着流一起响应到前面
HttpHeaders headers = new HttpHeaders();
// 设置响应头,以流的形式下载文件,适用于任意格式的文件
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 文件名包含中文时乱码问题
// headers.setContentDispositionFormData("attachment", new String(exportFile.getName().getBytes("utf-8"), "ISO8859-1"));
// 设置文件下载的名称
headers.setContentDispositionFormData("attachment", objectName);
// 构建响应实体,返回文件内容和响应头,状态码为CREATED
return new ResponseEntity<byte[]>(is.toString().getBytes(),
headers, HttpStatus.CREATED);
}
controller
@PostMapping("/del")
public FileUploadResult del(String fileName){
FileUploadResult delete = useOss.delete(fileName);
return delete;
}
@GetMapping("/download")
public ResponseEntity<byte[]> download(String fileName){
ResponseEntity<byte[]> responseEntity = useOss.exportOssFile(fileName);
return responseEntity;
}
}