引言
文件上传,当我们选择了某一个图片文件之后,这个文件就会上传到服务器,从而完成文件上传的操作,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程。
文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。
题外话
这个业务场景我在面试的时候被问到过,所以大家可以好好捋一捋流程 (实习面试)
目录
文件上传
前端程序
上传文件的原始form表单,要求表单必须具备以下三点(上传文件页面三要素):
<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>
上传文件页面的三要素
- 表单必须有file域,用于选择要上传的文件
<input type="file" name="image"/>
-
表单提交方式必须为POST
通常上传的文件会比较大,所以需要使用 POST 提交方式
-
表单的编码类型enctype必须要设置为:multipart/form-data
普通默认的编码格式是不适合传输大型的二进制数据的,所以在文件上传时,表单的编码格式必须设置为multipart/form-data
后端程序
分析
-
首先在服务端定义这么一个controller,用来进行文件上传,然后在controller当中定义一个方法来处理
/upload
请求 -
在定义的方法中接收提交过来的数据 (方法中的形参名和请求参数的名字保持一致)
-
用户名:String name
-
年龄: Integer age
-
文件: MultipartFile image
Spring中提供了一个API:MultipartFile,使用这个API就可以来接收到上传的文件
-
UploadController代码 :
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age,@RequestParam("image") MultipartFile file) {
log.info("文件上传:{},{},{}",username,age,image);
return Result.success();
}
}
这里因为跟前端<input type="file" name="image"/>这段代码的name=image跟方法新参名file对不上所以使用了@RequestParam("image")来进行参数绑定
推荐名字一致
这里接收到的文件是临时数据,所以需要进行存储
文件存储的俩种方式
本地存储
前面我们已分析了文件上传功能前端和后端的基础代码实现,文件上传时在服务端会产生一个临时文件,请求响应完成之后,这个临时文件被自动删除,并没有进行保存。下面呢,我们就需要完成将上传的文件保存在服务器的本地磁盘上。
代码实现:
-
在服务器本地磁盘上创建images目录,用来存储上传的文件(例:E盘创建images目录)
-
使用MultipartFile类提供的API方法,把临时文件转存到本地磁盘目录下
MultipartFile 常见方法:
String getOriginalFilename(); //获取原始文件名
void transferTo(File dest); //将接收的文件转存到磁盘文件中
long getSize(); //获取文件的大小,单位:字节
byte[] getBytes(); //获取文件内容的字节数组
InputStream getInputStream(); //获取接收到的文件内容的输入流
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws IOException {
log.info("文件上传:{},{},{}",username,age,image);
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//构建新的文件名
String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//获取文件文件扩展名
String newFileName = UUID.randomUUID().toString()+extname;//随机名+文件扩展名
//将文件存储在服务器的磁盘目录
image.transferTo(new File("E:/images/"+newFileName));
return Result.success();
}
}
代码解释
获取原始文件名,例如1.jpg
String originalFilename = image.getOriginalFilename();获取文件后缀名,例如.jpg
String extname = originalFilename.substring(originalFilename.lastIndexOf("."));通过UUID生成随机名保证每次上传文件时文件名都不重复 再根据文件后缀进行拼接
String newFileName = UUID.randomUUID().toString()+extname;//随机名+文件扩展名
如果直接存储在服务器的磁盘目录中,存在以下缺点:
-
不安全:磁盘如果损坏,所有的文件就会丢失
-
容量有限:如果存储大量的图片,磁盘空间有限(磁盘不可能无限制扩容)
-
无法直接访问
为了解决上述问题呢,可以使用阿里云OSS来解决
阿里云OSS
阿里云的注册登录准备工作
可以看这篇文章阿里云OSS-文件上传-CSDN博客
具体使用
举个案例
在新增员工的时候,上传员工的图像,而之所以需要上传员工的图像,是因为将来我们需要在系统页面当中访问并展示员工的图像。而要想完成这个操作,需要做两件事:
需要上传员工的图像,并把图像保存起来(存储到阿里云OSS)
访问员工图像(通过图像在阿里云OSS的存储地址访问图像)
OSS中的每一个文件都会分配一个访问的url,通过这个url就可以访问到存储在阿里云上的图片。所以需要把url返回给前端,这样前端就可以通过url获取到图像。
引入阿里云OSS上传文件工具类(由官方的示例代码改造而来)
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
@Component
public class AliOSSUtils {
private String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
private String accessKeyId = "LTAI5t9MZK8iq5T2Av5GLDxX";
private String accessKeySecret = "C0IrHzKZGKqU8S7YQcevcotD3Zd5Tc";
private String bucketName = "web-framework01";
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile multipartFile) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = multipartFile.getInputStream();
// 避免文件覆盖
String originalFilename = multipartFile.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
UploadController代码:
import com.itheima.pojo.Result;
import com.itheima.utils.AliOSSUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@Slf4j
@RestController
public class UploadController {
@Autowired
private AliOSSUtils aliOSSUtils;
@PostMapping("/upload")
public Result upload(MultipartFile image) throws IOException {
//调用阿里云OSS工具类,将上传上来的文件存入阿里云
String url = aliOSSUtils.upload(image);
//将图片上传完成后的url返回,用于浏览器回显展示
return Result.success(url);
}
}
测试
选择图片
查看响应
这里响应的就是url地址,可以通过这个url访问图片