为什么使用云存储的方式?
什么是OSS对象存储服务?
阿里云对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存储服务。
- 其数据设计持久性不低于99.9999999999%(12个9),服务可用性(或业务连续性)不低于99.995%。
- OSS具有与平台无关的RESTful API接口,您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 您可以使用阿里云提供的API、SDK接口或者OSS迁移工具轻松地将海量数据移入或移出阿里云OSS。
图片存储解决方案
实现图片上传服务,需要有存储的支持,那么我们的解决方案将以下几种:
直接将图片保存到服务的硬盘
优点:开发便捷,成本低
缺点:扩容困难
使用分布式文件系统进行存储
优点:容易实现扩容
缺点:开发复杂度稍大(有成熟的产品可以使用,比如:FastDFS)
使用nfs做存储
优点:开发较为便捷
缺点:需要有一定的运维知识进行部署和维护
使用第三方的存储服务
优点:开发简单,拥有强大功能,免维护
缺点:付费
如果直接存储在服务器的磁盘目录中,存在以下缺点:
- 不安全:磁盘如果损坏,所有的文件就会丢失
- 容量有限:如果存储大量的图片,磁盘空间有限(磁盘不可能无限制扩容)
- 无法直接访问
为了解决上述问题呢,通常有两种解决方案:
- 自己搭建存储服务器,如:fastDFS 、MinIO
- 使用现成的云服务,如:阿里云,腾讯云,华为云
接下来介绍一下当前要使用的阿里云oss对象存储服务具体的使用步骤:
1.开通 “ 对象存储OSS ” 服务
(1)申请阿里云账号;
(2)实名认证;
(3)开通 “ 对象存储OOS ” 服务;
(4)进入管理控制台;
2.创建Bucket
输入名称,选择区域,选择标准存储、公共读;
使用OSS,首先需要创建Bucket,Bucket翻译成中文是水桶的意思,把存储的图片资源看做是水,想要盛水必须得有桶,就是这个意思了。存储空间(Bucket)是用于存储对象(Object)的容器,可以存储若干文件。所以在上传任何文件到OSS之前,您必须先创建存储空间。
OSS 提供权限控制 ACL(Access Control List),您可以在创建存储空间的时候设置相应的 ACL 权限控制,也可以在创建之后修改 ACL。如果不设置 ACL,默认值为私有。
OSS ACL 提供存储空间级别和文件级别的权限访问控制,目前有三种访问权限:
- 私有:只有该存储空间的拥有者可以对该存储空间内的文件进行读写操作,其他人无法访问该存储空间内的文件。
- 公共读:只有该存储空间的拥有者可以对该存储空间内的文件进行写操作,任何人(包括匿名访问者)可以对该存储空间中的文件进行读操作。
- 公共读写:任何人(包括匿名访问者)都可以对该存储空间中的文件进行读写操作,所有这些操作产生的费用由该存储空间的拥有者承担,请慎用该权限。
3.创建accessKeys
访问密钥AccessKey(简称AK)是阿里云提供给阿里云用户的永久访问密钥,用于通过开发工具(API、CLI、SDK、Cloud Shell、Terraform等)访问阿里云时的身份验证。
AccessKey包括AccessKey ID和AccessKey Secret,需要一起使用,请妥善保管。具体如下:
AccessKey ID:用于标识用户。
AccessKey Secret:用于验证用户的密钥。AccessKey Secret必须保密。
4.使用SDK
对象存储(OSS)-阿里云帮助中心 (aliyun.com)https://help.aliyun.com/zh/oss/?spm=a2c4g.11186623.0.0.2c9d39b9utCtWk参考文档:如何使用JavaSDK简单上传文件_对象存储(OSS)-阿里云帮助中心简单上传是指通过PutObject方法上传单个文件(Object)。简单上传包括流式上传和文件上传,流式上传使用InputStream作为OSS文件的数据源,文件上传使用本地文件作为OSS文件的数据源。本文介绍如何使用流式上传和文件上传方式上传文件。https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.84778.0.0.6cd73e06WozWvy
endpoint:endpoint是创建Bucket所选的地域节点,可在Bucket “ 概述 ” 中查看;
endpoint以杭州为例:String endpoint = "oss-cn-shanghai.aliyuncs.com";
access-key-id: 阿里云账号AccessKey;
access-key-secret: 阿里云账号AccessKey对应的秘钥;
bucket-name: 存储空间名称,Bucket名称;
JAVA通过阿里云OSS存储实现图片上传功能
一、依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.sdk.oss}</version>
</dependency>
二、yml配置
sky:
alioss:
# endpoint填写Bucket所在地域对应的endpoint
# 以华东1(杭州)为例: endpoint填写为 https://oss-cn-hangzhou.aliyuncs.com
endpoint: https://*************** 或者 "oss-cn-hangzhou.aliyuncs.com"
# 根据自己的帐号配置
access-key-id: ***********
# 根据自己的帐号配置
access-key-secret: ***********
# 存储空间名称,存储空间是您用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间
bucket-name: ***********
三、创建类读取yml配置文件
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOSSProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
四、将阿里云给出的上传代码封装成工具类
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
/**
* 文件上传
*
* @param bytes
* @param objectName
* @return
*/
public String upload(byte[] bytes, String objectName) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
} 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();
}
}
// 文件访问路径规则 https://BucketName.Endpoint/ObjectName
StringBuilder stringBuilder = new StringBuilder("https://");
stringBuilder
.append(bucketName)
.append(".")
.append(endpoint)
.append("/")
.append(objectName);
log.info("文件上传到:{}", stringBuilder.toString());
return stringBuilder.toString();
}
}
五、编写OOS配置类
注意:在编写OSS配置类的时候,记得在方法上面加上@ConditionalOnMissingBean注解,这是保证项目运行过程中只会创建一次工具类对象。
import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置类,用于创建AliOssUtil对象
*/
@Configuration
@Slf4j
public class OssConfiguration {
@Bean
@ConditionalOnMissingBean
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}
六、编写定义文件上传接口,将图片连接返回前端即可查看图片
import com.sky.constant.MessageConstant;
import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
/**
* 通用接口
*/
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {
@Autowired
private AliOssUtil aliOssUtil;
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result<String> upload(MultipartFile file){
log.info("文件上传:{}",file);
try {
// 原始文件名
String originalFilename = file.getOriginalFilename();
// 截取原始文件名的后缀 dfdfdf.png
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
// 构造新文件名称
String objectName = UUID.randomUUID().toString() + extension;
// 文件的请求路径
String filePath = aliOssUtil.upload(file.getBytes(), objectName);
//将图片连接返回前端
return Result.success(filePath);
} catch (IOException e) {
log.error("文件上传失败:{}", e);
}
return Result.error(MessageConstant.UPLOAD_FAILED);
}
}