在项目中有大量需要存储的图片、视频以及大文件,传统的方法是放在数据库、服务器上传下载目录、第三方FTP服务器,这些处理方法都存在一般弊端。此外传统的数据库或文件方式存储系统文件时,对于流媒体、超大文件的访问仍然无法满足应用要求;此外随文件增多,没有好的文件管理工具,对于数据备份、数据迁移,甚至当今互联网应用需要的内容分析、内容检查都是无法实现的。因此,在当前在互联网环境下,分布式OSS是比较好的解决方案。
以下是几种方法的对比:
不同存储方式对比
当前阿里云OSS是行业使用比较普遍的对象存储提供商,下面以阿里云OSS访问为例,介绍在Spring Boot环境下的编程方法。
一、MAVEN项目依赖
com.aliyun.oss
aliyun-sdk-oss
3.8.1
二、建立组件文件
@Component
public class OSSFileUtils {
protected static final Logger log = LoggerFactory.getLogger(OSSFileUtils.class);
//构建访问的参数,这几个参数放在配置文件,注意这个配置文件带有秘钥,不要上传到Git,以免密码泄漏。
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.dir.prefix}")
private String prefix;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
/**
* 单个图片上传,并返回直接能够访问的绝对路径,
* @param file
* @return,直接访问的路径
*/
public String checkFile(String baseDir, MultipartFile file){
String fileUrl = uploadFile2Oss(baseDir,file);
return fileUrl.trim();
}
/**
* 上传图片,检查图片大小,组合文件在于储路径
* @param file
* @return
*/
public String uploadFile2Oss(String baseDir, MultipartFile file) {
if (file.getSize() > 1024 * 1024 *20) {
return "图片太大";
}
String fileName = baseDir + '/' + extractFilename(file);
System.out.println(fileName);
try {
InputStream inputStream = file.getInputStream();
return uploadFile2OSS(inputStream, fileName);
} catch (Exception e) {
return "上传失败";
}
}
/**
* 编码文件名,文件按年、月、日目录方式存储存,减少单个目录文件数量,并对文件名进行转换,保留原文件的扩展名
*/
public static final String extractFilename(MultipartFile file)
{
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
fileName = DateUtils.datePath() + "/" + encodingFilename(fileName) + "." + extension;
return fileName;
}
/**
* 正式上传图片文件,并返回获取文件绝对路径
* @param instream
* @param fileName
* @return
*/
private String uploadFile2OSS(InputStream instream, String fileName) {
String ret = "";
try {
//创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(instream.available());
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentType(getcontentType(fileName.substring(fileName.lastIndexOf("."))));
objectMetadata.setContentDisposition("inline;filename=" + fileName);
//上传文件,新版本的改为OSSClientBuilder了
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, instream, objectMetadata);
// 关闭OSSClient。
ossClient.shutdown();
//组合访问绝对路径,此种直接访问方式需要打开OSS的跨域设置,后面介绍,此外此部访问文式存在倒链问题,需要在OSS安全设置中设置哪些网站能够访问。
ret = "https://" + bucketName + "." + endpoint + "/" + fileName;
//下面是生成安全的访问路径链接,包括有效时间
//ret = ossClient.generatePresignedUrl(bucketName, fileName, expiration).toString();
System.out.println(ret);
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
try {
if (instream != null) {
instream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return ret;
}
/**
* 获得对象的安全访问url链接
* @param key
* @return
*/
public String getUrl(String key) {
// 设置URL过期时间为10年 3600l* 1000*24*365*10
Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10);
// 生成URL
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
if (url != null) {
return url.toString();
}
return null;
}
/**
* 批量多图片上传
* @param fileList
* @return 返回的是带限制的访问地址,后续使用需要检查
*/
public String checkList(String baseDir,List fileList) {
String fileUrl = "";
String str = "";
String photoUrl = "";
for(int i = 0;i< fileList.size();i++){
fileUrl = uploadFile2Oss(baseDir,fileList.get(i));
//获取带认证的访问链接
str = getFileUrl(fileUrl);
if(i == 0){
photoUrl = str;
}else {
photoUrl += "," + str;
}
}
return photoUrl.trim();
}
}
三、组件调用上传
OSS组件调用比较简单,将OSSFileUtils引用过来即可台调用
@Autowired
private OSSFileUtils ossFileUtils;
/**
* 用户头像图片后台文件上传,采用MultipartFile文件协议
*/
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException
{
if (!file.isEmpty())
{
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
//将文件保存在服务器
//String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file);
//将文件保存在阿里云
String avatar = ossFileUtils.checkFile("avatar", file);
//将绝对访问路径存在数据库
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
{
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);
// 更新打开页面缓存的用户头像
loginUser.getUser().setAvatar(avatar);
tokenService.setLoginUser(loginUser);
return ajax;
}
}
return AjaxResult.error("上传图片异常,请联系管理员");
}
## 四、VUE前端调用
uploadImg() {
this.$refs.cropper.getCropBlob(data => {
let formData = new FormData();
formData.append("avatarfile", data);
uploadAvatar(formData).then(response => {
if (response.code === 200) {
this.open = false;
//this.options.img = process.env.VUE_APP_BASE_API + response.imgUrl;//改阿里OSS后直接用绝对咱径
this.options.img = response.imgUrl;
this.msgSuccess("修改成功");
} else {
this.msgError(response.msg);
}
this.$refs.cropper.clearCrop();
});
});
},
前端用formData方式上传。另前端用vue-cropper组件对图片进行裁剪,只上传先中需要部分。
五、OSS跨域设置
由于是使用的跨域访问的方式进行的文件上传操作所以先要设置允许跨域访问,在oss的访问规则中添加“Access-Control-Allow-Origin”的访问权限 response的头部信息中添加“Access-Control-Allow-Origin”。
完成上述工作后,程序中基本上可以将文件上传OSS,并进行访问。