目录
- 背景
- 控制台操作
- 开通OSS服务
- 创建存储空间
- 项目工程准备
- 概述
- 新建文件管理模块
- 把文件上传到OSS的三种方案
- 通过官方文档完成demo上传
- 官方文档找JavaSDK
- 文件上传思路
- 代码
背景
为什么要交给第三方文件管理服务管理?
最传统的的文件管理方案是把文件存储到项目中本地 “/webapp/upload/”,把文件的地址保存到数据库,客户端获取到图片的地址就可以浏览或者下载文件。这种管理方式是把文件交给应用本身来处理,文件不仅要占用应用服务器的空间资源,文件的读取以及把文件响应给客户端给应用服务器也带来很大的压力,并且在分布式/集群环境中存在文件不共享的问题。
在分布式/集群环境中需要采用统一的文件处理方案,文件不需要应用本身来管理,而是交给专门的文件服务器来实现文件的上传,下载以及删除操作,这样的好处是应用服务器不再处理图片压力减轻,图片不会占用应用服务器本身的空间资源,并且统一的文件服务器实现的文件的共享,所有的微服务都可以通过文件服务器来上传和下载文件。
另外第三方文件管理服务服务完善,包括图片鉴黄,处理政治,邪教,传销等敏感信息,图片瘦身,水印等。
为什么没再用fastDFS,大城市大企业很少用,成本也不低。自己搭的出了问题需要自己维护,没有第三方稳定。
阿里云OSS处理方案:https://blog.csdn.net/u014494148/article/details/108858360
官方文档:https://help.aliyun.com/document_detail/31886.html
控制台操作
开通OSS服务
阿里云官网 - 产品 - 存储 - 对象存储 OSS - 立即开通/管理控制台(由于已经开通)
创建存储空间
Bucket 列表 - 创建bucket(注意名称不要写中文 最好英文字母加数字 也可以加横杠)
进入创建好的存储空间,可以先测试一下使用功能
项目工程准备
概述
在微服务架构中把文件相关操作功能单独做一个服务,系统中所有模块只要涉及文件的上传都走同一个服务
存在两种情况:1.前端直接访问文件服务,2.服务之间文件的上传,(用到feign,融合了ribbon和hystrix)
新建文件管理模块
作为父工程把src删掉,
设置打包方式:< packaging >pom< /packaging >,这样不会打成jar包,只是去维护工程中的公共坐标
把文件上传到OSS的三种方案
1.通过web前端直接上传到OSS服务器,这种方式意味着需要把OSS配置参数写死在JS中,有安全隐患
2.通过web前端把文件上传到后端服务器,后端服务器再把文件上传到OSS,这种方式虽然没有了暴露OSS配置参数的安全隐患,但是文件通过后端中转了一次在上传,性能不高,而且增加了服务的压力.影响不大,我们用第二种
3.OSS最佳实践:后端服务签名后前端直传,即:依然是web前端直接把文件上传到OSS服务器,只是在上传文件之前先请求后端获取OSS配置签名信息,web前端拿到签名信息后和文件一起上传到OSS服务器,这样一来,文件上传既不需要通过后台服务器中转一次,也不会有暴露OSS配置参数的风险。
通过官方文档完成demo上传
官方文档找JavaSDK
https://help.aliyun.com/document_detail/31886.html
文件上传思路
1.前端发起一个上传的请求
2.前端将上传的文件以IO流的方式,传到后台
3.后台会以MultipartFile的数据类型来接收
4.进行文件保存
使用OSS的对象存储来实现
代码
application.yml
server:
port: 1050
file:
alicloud:
bucketName: hrm-obj-ming #上传空间bucket
accessKey: aaaaaaaaaaaaaaaaaaaaa #key
secretKey: sssssssssssssssssssss #秘钥
endpoint: https://oss-cn-beijing.aliyuncs.com #上传端点
AlicloudOSSProperties
@Data
@Component //这个注解不能省略。将这个对象的创建过程交给Spring管理。只有这样你才能在需要使用它的地方,通过@autowired进行注入
@ConfigurationProperties(prefix = "file.alicloud") //读取application.yml中自定义的属性,并设置它的前缀
public class AlicloudOSSProperties {
private String bucketName;
private String accessKey;
private String secretKey;
private String endpoint;
}
FileController
@RestController
@RequestMapping("/file")
public class FileController {
/* @Value("${file.alicloud.buketName}")
private String buketName;
@Value("${file.alicloud.accessKey}")
private String accessKey;
@Value("${file.alicloud.secretKey}")
private String secretKey;
@Value("${file.alicloud.endpoint}")
private String endpoint;*/
@Autowired
private AlicloudOSSProperties alicloudOSSProperties;
@PostMapping("/upload1")
private AjaxResult upload1(MultipartFile file,String objectPath){
// Endpoint按实际情况填写。
String endpoint = "https://oss-cn-beijing.aliyuncs.com";
String accessKeyId = "kkkkkkkkkkkkkkkkkkk";
String accessKeySecret = "sssssssssssssssssssssss";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
try {
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
} catch (com.aliyuncs.exceptions.ClientException e) {
e.printStackTrace();
}
// 填写Bucket名称
String bucketName = "hrm-obj-ming";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String suffix = StringUtils.substringAfter(file.getOriginalFilename(), ".");
String objectName = objectPath + resetFileName() + "." + suffix;
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
String localPath = "D:/devPicture/pethome/";
String localFileWholeName = localPath + file.getOriginalFilename();
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
try {
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new File(localFileWholeName));
// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// ObjectMetadata metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上传文件。
PutObjectResult result = ossClient.putObject(putObjectRequest);
System.out.println("result = " + result);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return new AjaxResult().setMessage("上传成功了");
}
@PostMapping("/upload2")
public AjaxResult upload2(String objectPath,MultipartFile file) throws Exception {
/* // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
String endpoint = "https://oss-cn-beijing.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
String accessKeyId = "kkkkkkkkkkkkkkkk";
String accessKeySecret = "ssssssssssssssss";*/
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(alicloudOSSProperties.getEndpoint(), alicloudOSSProperties.getAccessKey(), alicloudOSSProperties.getSecretKey());
// 填写Byte数组。
byte[] content = file.getBytes();
// 填写Bucket名称和Object完整路径。Object完整路径中不能包含Bucket名称。
//注意:所有有关字符串的操作,尽量使用commons-lang3包中stringutils工具类
//abc.png-----拿到.png
String suffix = StringUtils.substringAfter(file.getOriginalFilename(), ".");
String ossWholeFileName = objectPath + resetFileName() + "." + suffix;
ossClient.putObject(alicloudOSSProperties.getBucketName(), ossWholeFileName, new ByteArrayInputStream(content));
// 关闭OSSClient。
ossClient.shutdown();
return AjaxResult.me().setResultObj(ossWholeFileName);
}
/**
* 生成一个唯一的文件的名称:年+月+日+随机串
* @return
*/
private String resetFileName() {
//如何获取年月日
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String format = sdf.format(new Date());
//再通过UUID得到一个随机且唯一的字符串
String randomStr = StringUtils.replace(UUID.randomUUID().toString(), "-", "");
return format + randomStr;
}
}
pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>