这就不用说了,从阿里云平台花钱买OSS对象存储就行了。
然后在bucket列表中创建自己使用的bucket即可。
比如创建:oss-test
然后从概览中找到 Access Key
然后就可以看到对应的id和key了。
这个时候,就有了四个参数
1、access-key:就是刚才的 AccessKey ID
2、secret-key:页面点击‘查看 Secret’就能看的到了
3、bucket:新建的桶的名称oss-test
4、endpoint:页面截图,选择自己的桶就能看到对应的endpoint
本篇代码,主要做的两件事:
1、文件上传至OSS
2、对服务器磁盘文件转存至OSS
在application.yml中增加配置
#阿里云oss
alibaba:
cloud:
access-key: **********
secret-key: **********
oss:
endpoint: oss********.aliyuncs.com
bucket: *******
config:
# oss开关 0:关 1:开
oss-switch: 1
# oss 开始使用时间
oss-date: 2022-01-01
# oss 文件夹目录
oss-directory: image,attach,video,audio,template
# oss title
oss-title: https://
# oss business directory
business-directory: user,org
非常非常重要:在配置的时候上面4个重要参数的位置,以及名称一定不能修改,否则会报找不到endPoint的错
新建 YmlConfigBean.java
package com.upload.bean;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 配置文件参数读取
*/
@Component
public class YmlConfigBean {
// 阿里云OSS 申请的 key id
public volatile static String OSS_KEY_ID;
// 阿里云OSS 申请的 key secret
public volatile static String OSS_KEY_SECRET;
// 阿里云OSS endpoint
public volatile static String OSS_ENDPOINT;
// 阿里云OSS bucket
public volatile static String OSS_BUCKET;
// 阿里云OSS 开关 0 关 1 开
public volatile static String OSS_SWITCH;
// 阿里云OSS 开始使用时间
public volatile static String OSS_DATE;
// 阿里云OSS 文件夹目录
public volatile static List<String> OSS_DIRECTORY;
// 阿里云OSS 开始使用时间
public volatile static String OSS_TITLE;
//业务文件类别
public volatile static List<String> BUSINESS_DIRECTORY;
}
新建 YmlConfigBeanMethod.java
package com.upload.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class YmlConfigBeanMethod {
@Value("#{'${alibaba.cloud.access-key}'}")
private void setOssKeyId(String key) {
YmlConfigBean.OSS_KEY_ID = key;
}
@Value("#{'${alibaba.cloud.secret-key}'}")
private void setOssKeySecret(String key) {
YmlConfigBean.OSS_KEY_SECRET = key;
}
@Value("#{'${alibaba.cloud.oss.endpoint}'}")
private void setOssEndpoint(String key) {
YmlConfigBean.OSS_ENDPOINT = key;
}
@Value("#{'${alibaba.cloud.oss.bucket}'}")
private void setOssBucket(String key) {
YmlConfigBean.OSS_BUCKET = key;
}
@Value("#{'${alibaba.config.oss-switch}'}")
private void setOssSwitch(String key) {
YmlConfigBean.OSS_SWITCH = key;
}
@Value("#{'${alibaba.config.oss-date}'}")
private void setOssDate(String key) {
YmlConfigBean.OSS_DATE = key;
}
@Value("#{'${alibaba.config.oss-directory}'.split(',')}")
public void setOssDirectory(List<String> key){
YmlConfigBean.OSS_DIRECTORY = key;
}
@Value("#{'${alibaba.config.oss-title}'}")
private void setOssTitle(String key) {
YmlConfigBean.OSS_TITLE = key;
}
@Value("#{'${alibaba.config.business-directory}'.split(',')}")
public void setBusinessDirectory(List<String> key){
YmlConfigBean.BUSINESS_DIRECTORY= key;
}
}
建一个统一的DTO Result.java
package com.upload.bean;
/**
* 统一的DTO
*/
public class Result<T> {
private boolean success = true;
private Integer code = 0;
private String msg;
private T data;
public boolean isSuccess() {
return this.code == 0;
}
public void setSuccess(boolean success) {
this.success = success;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
if(code != 0){
success = false;
}
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Result() {
super();
}
public Result(boolean success) {
this.success = success;
if(success){
code = 0;
}
}
public Result(int code) {
super();
success = false;
this.code = code;
}
public Result(String msg) {
success = false;
this.msg = msg;
}
public Result(boolean success, String msg) {
super();
this.success = success;
this.msg = msg;
}
public Result(int code, String msg) {
super();
success = false;
this.code = code;
this.msg = msg;
}
/*设置返回为成功*/
public static Result ok() {
return new Result();
}
/*设置返回为成功,同时设置返回数据*/
public Result<T> ok(T data) {
Result<T> result = new Result<T>();
result.setData(data);
return result;
}
/*设置返回为失败,默认未知异常*/
public static Result error() {
return error(500, "未知异常,请联系管理员!");
}
/*设置返回为失败,同时设置异常信息*/
public static Result error(String msg) {
return error(500, msg);
}
/*设置返回为失败,同时设置code,msg*/
public static Result error(Integer code,String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
public static Result<Object> error(Integer code,String msg,Object data) {
Result<Object> result = new Result<Object>();
result.setData(data);
result.setCode(code);
result.setMsg(msg);
return result;
}
public static Result success(Object data) {
Result<Object> result = new Result<Object>();
result.setData(data);
return result;
}
public static Result success(String msg) {
Result result = new Result();
result.setMsg(msg);
return result;
}
}
常量配置好之后,方便后续直接使用配置文件的中内容。
新建 OssUploadController.java
package com.upload;
import com.upload.service.IOssUploadService;
import com.upload.bean.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
*
* 阿里云OSS 前端控制器
*
*/
@Controller
public class OssUploadController {
@Resource
private IOssUploadService ossUploadService;
/**
* OSS文件上传
*/
@RequestMapping("/uploadOss")
@ResponseBody
public Result upload(@RequestParam String ossDirectory, @RequestParam String businessDirectory,
@RequestParam MultipartFile file, HttpServletRequest request) {
return ossUploadService.upload(ossDirectory, businessDirectory, file, request);
}
/**
* OSS文件迁移
*/
@RequestMapping("/migrationOss")
@ResponseBody
public Result doDataMigration(@RequestParam String ossDirectory, @RequestParam String businessDirectory) {
return ossUploadService.doDataMigration(ossDirectory, businessDirectory, true);
}
}
新建 IOssUploadService.java
package com.upload.service;
import com.upload.bean.Result;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
/**
*
* 阿里云OSS 服务类
*/
public interface IOssUploadService {
Result upload(String ossDirectory, String businessDirectory, MultipartFile file, HttpServletRequest request);
Result doDataMigration(String ossDirectory, String businessDirectory, boolean threadFlag);
}
新建 OssUploadServiceImpl.java
package com.upload.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CannedAccessControlList;
import com.upload.bean.Result;
import com.upload.bean.YmlConfigBean;
import com.upload.entity.FileUpload;
import com.upload.service.IOssUploadService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Slf4j
@Service
public class OssUploadServiceImpl implements IOssUploadService {
// @Resource
// private UploadMapper uploadMapper;
private final static String ROOT_PATH = "/data/file/";
/**
* 对外提供的文件上传
* @param ossDirectory OSS所属模块
* @param fileDir 文件所属模块
* @param file 文件
* @param request 请求
*/
@Override
public Result upload(String ossDirectory, String fileDir, MultipartFile file, HttpServletRequest request) {
if (file.isEmpty()) {
return Result.error("不能上传空文件");
}
String originalFilename = file.getOriginalFilename();
Long fileBytes = file.getSize();
if (Strings.isBlank(originalFilename) || 0L == fileBytes) {
return Result.error("不能上传空文件");
}
if(Strings.isBlank(ossDirectory)){
ossDirectory = "image";
} else {
if (!YmlConfigBean.OSS_DIRECTORY.contains(ossDirectory.startsWith("/") ? ossDirectory.substring(1) : ossDirectory)) {
log.info("文件上传目录非法,非法目录:"+ ossDirectory);
return Result.error("文件上传目录非法,请更改文件上传目录");
}
}
if (!YmlConfigBean.BUSINESS_DIRECTORY.contains(fileDir.startsWith("/") ? fileDir.substring(1) : fileDir)) {
log.info("文件上传目录非法,非法目录:"+ fileDir);
return Result.error("文件上传目录非法,请更改文件上传目录");
}
String fileMd5;
try {
fileMd5 = DigestUtils.sha256Hex(file.getInputStream());
} catch (Exception e) {
log.error(file.getOriginalFilename() + "验证文件md5失败",e);
return Result.error("当前验证文件md5失败");
}
Result<String> result = new Result<>();
String uploadUrl = null;
String newName = null;
OSS ossClient = getOSSClient();
try {
//获取上传文件流
InputStream inputStream = file.getInputStream();
//构建日期路径:/2019/02/26/文件名
String fileType = originalFilename.split("\\.")[originalFilename.split("\\.").length - 1];
newName = DateFormatUtils.format(new Date(), "yyMMddHHmmss") +
(new SecureRandom().nextInt(100) + 100) + "." + fileType;
String newFileDir = (fileDir.startsWith("/") ? fileDir.substring(1) : fileDir).trim().replace(" ","") +
getDatePath();
String fileUrl = ossDirectory + "/" + newFileDir + newName;
// 上传文件 (上传文件流的形式)
ossClient.putObject(YmlConfigBean.OSS_BUCKET, fileUrl, inputStream);
//获取url地址
uploadUrl = YmlConfigBean.OSS_TITLE + YmlConfigBean.OSS_BUCKET + "." + YmlConfigBean.OSS_ENDPOINT + "/" + fileUrl;
} catch (IOException e) {
log.error("上传OSS失败,文件名:" + originalFilename);
throw new RuntimeException("上传OSS失败");
} finally {
// 关闭OSSClient。
if (ossClient != null) {
ossClient.shutdown();
}
}
//文件上传实体属性赋值,数据入库
try {
log.info("原文件名称:" + originalFilename + "\n" + "文件MD5码:" + fileMd5 + "\n" + "新文件名称:" + newName + "\n" +
"文件路径:" + uploadUrl + "\n" + "文件大小:" + fileBytes);
// uploadMapper.doUploadInsert(null);
} catch (Exception e) {
log.error("上传文件,数据库记录失败", e);
}
result.setData(uploadUrl);
return result;
}
/**
* 文件迁移
* @param ossDirectory 需要同步的OSS文件夹
* @param businessDirectory 业务文件路径,以此查询表中数据
* @param threadFlag 是否开启异步线程
* @return Result
*/
@Override
public Result doDataMigration(String ossDirectory, String businessDirectory, boolean threadFlag){
if(Strings.isBlank(businessDirectory)) return Result.error("路径不能为空");
// List fileUploads = uploadMapper.selectList();
List<FileUpload> fileUploads = new ArrayList<>();
if(null == fileUploads || fileUploads.size() == 0) return Result.error("暂无数据");
if(threadFlag){
Thread thread = new Thread(()->{
doSonThread(fileUploads, ossDirectory);
});
thread.start();
} else {
doSonThread(fileUploads, ossDirectory);
}
return Result.success("文件迁移中,请稍后查看!");
}
private void doSonThread(List<FileUpload> fileUploads, String ossDirectory){
if(Strings.isBlank(ossDirectory)) {
ossDirectory = "image";
}
String filePath = null;
for(FileUpload fileUpload : fileUploads){
if(Strings.isBlank(fileUpload.getFilePath()) || Strings.isBlank(fileUpload.getFileName())){
continue;
}
filePath = uploadFileAndDeleteByPath(ROOT_PATH + fileUpload.getFilePath(), fileUpload.getFileName(), ossDirectory);
//更新新的路径入库
fileUpload.setFilePathNew(filePath);
// uploadMapper.doUploadUpdate(fileUpload);
}
}
public static String uploadFileAndDeleteByPath(String path, String fileName, String ossDirectory) {
File file = new File(path + File.separator + fileName);
return uploadFileAndDeleteByFile(file, ossDirectory);
}
public static String uploadFileAndDeleteByFile(File file, String ossDirectory) {
if (file.isDirectory()) return null;
String filePath = null;
Boolean deleteFlag = false;
try {
filePath = uploadHistory(ossDirectory, file.getPath(), file);
deleteFlag = checkExists(filePath.substring(filePath.indexOf(YmlConfigBean.OSS_ENDPOINT) + 1 + YmlConfigBean.OSS_ENDPOINT.length(), filePath.length()));
} catch (IOException e) {
log.error("上传OSS失败,文件名:" + file.getName());
throw new RuntimeException("上传OSS失败");
}
if(deleteFlag){
file.delete();
}
return filePath;
}
public static String uploadHistory(String ossDirectory, String businessDirectory, File file) throws IOException{
String filename = file.getName();
OSS ossClient = getOSSClient();
//获取上传文件流
InputStream inputStream = new FileInputStream(file);
//构建日期路径:/2019/02/26/文件名
String fileType = filename.split("\\.")[filename.split("\\.").length - 1];
String newName = filename.substring(0, filename.indexOf(".")) + "_" + (new SecureRandom().nextInt(1000) + 1000) + "." + fileType;
String newFileDir = (businessDirectory.startsWith("/") ? businessDirectory.substring(1) : businessDirectory).trim().replace(" ","");
String fileUrl = ossDirectory + "/" + newFileDir + newName;
// 上传文件 (上传文件流的形式)
ossClient.putObject(YmlConfigBean.OSS_BUCKET, fileUrl, inputStream);
ossClient.shutdown();
//获取url地址
return YmlConfigBean.OSS_TITLE + YmlConfigBean.OSS_BUCKET + "." + YmlConfigBean.OSS_ENDPOINT + "/" + fileUrl;
}
public static boolean checkExists(String objectName) throws IOException{
OSS ossClient = getOSSClient();
Boolean flag = ossClient.doesObjectExist(YmlConfigBean.OSS_BUCKET, objectName);
ossClient.shutdown();
return flag;
}
public static OSS getOSSClient() {
OSS ossClient = new OSSClientBuilder().build(YmlConfigBean.OSS_ENDPOINT, YmlConfigBean.OSS_KEY_ID, YmlConfigBean.OSS_KEY_SECRET);
//判断oss实例是否存在:如果不存在则创建,如果存在则获取
if (!ossClient.doesBucketExist(YmlConfigBean.OSS_BUCKET)) {
//创建bucket
ossClient.createBucket(YmlConfigBean.OSS_BUCKET);
//设置oss实例的访问权限:公共读
ossClient.setBucketAcl(YmlConfigBean.OSS_BUCKET, CannedAccessControlList.PublicRead);
}
return ossClient;
}
/*public static void uploadFileAndDelete(File file) {
if (file.isDirectory()) {
String[] fileList = file.list();
if(null != fileList && fileList.length > 0){
//递归删除目录中的子目录下
File subFile = null;
for(String str : fileList){
subFile = new File(file, str);
log.info(subFile.getPath());
uploadFileAndDelete(subFile);
}
}
}
uploadFileAndDeleteByFile(file, null);
}*/
public static String getDatePath(){
return "/" + DateFormatUtils.format(new Date(), "yyyy-MM-dd").replaceAll("-","/") + "/";
}
}
在OSS上存储的路径为:OSS文件夹(image)+ 业务自定义文件夹名称(org)+ 年月日时间路径 + 重命名文件名称。
大致思路:
1、根据业务需要,查询数据库中的文件路径及名称,到服务器磁盘中查找该文件。
2、调用简易版的文件上传,上传至OSS中。
3、上传之后回调阿里自带方法doesObjectExist进行回溯校验,检查是否在OSS中存在。
4、如存在该文件,则删除服务器磁盘对应的文件即可。
因代码需根据业务数据来进行数据的查询等操作,可自行修改并验证。
参考资料:异常官网链接:https://help.aliyun.com/document_detail/32023.html
注意事项:在配置的时候上面4个重要参数的位置,以及名称一定不能修改,否则会报找不到endPoint的错
CSDN资源链接:https://download.csdn.net/download/qq_38254635/87352781
百度网盘资源链接:https://pan.baidu.com/s/1kl6KIntr1qvcJPG642DWAA?pwd=fsj2
提取码: fsj2
有什么不对的还望指正,书写不易,觉得有帮助就点个赞吧!