【开发指南】Spring Cloud集成阿里云OSS完成头像上传功能

552 阅读3分钟

1. 阿里云OSS设置

  1. 登录aliyun:www.aliyun.com/?utm_conten…

  2. 进入aliyun控制台

    image.png

  3. 进入OSS控制台

    image.png

  4. 新建Bucket

    Bucket为OSS的存储空间,在上传数据(例如文档、图片、音视频等)到OSS之前,您需要在OSS所支持的地域中创建一个存储空间(Bucket),然后将无限数量的对象(Object)上传到该Bucket中。

    image.png

  5. 输入Bucket信息

    image.png

2. 上传默认头像

  1. 进入Bucket,新建一个目录

    image.png

  2. 进入该目录,点击上传文件

    image.png

  3. 选择文件上传

    image.png

  4. 将默认头像的URL作为数据库avatar字段的默认值

    image.png

3. 获取AccessKeys

  1. 进入AccessKeys管理

    image.png

  2. 开始使用子用户AccessKeys

    阿里云给每一个账户提供一个全局的AccessKey,但是如果该AccessKey泄露,那么该用户在阿里云上所拥有的所有产品的使用权限都会暴露出去。所以选择使用子用户的AccessKey,来讲所有的使用权限细分,每一个阿里云的业务都可以定制独立的AccessKey。

    image.png

  3. 创建用户组

    image.png

  4. 添加用户

    image.png

    image.png

  5. 将用户添加进入用户组

    image.png

    image.png

  6. 给用户组分配权限

    还可以给每一个用户分配权限,一般比较简单的项目只需要给用户组分配权限即可。

    image.png

    image.png

  7. 获取AccessKey

    AccessKey获取后需要保存起来,如果丢失,重新创建一个新的AccessKey即可。

    image.png

    20210712103526.png

4. OSS环境配置

  1. 服务中引入OSS依赖

    <!--  阿里云对象存储  -->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>2.8.3</version>
    </dependency>
    
  2. 查看endpoint

    image.png

  3. 查看accessKeyId和access

    见3.7

  4. 查看bucketName

    image.png

  5. 服务的application.yml添加配置

# 阿里云oss配置
aliyun:
  oss:
    file:
      bucketname: postilhub
      endpoint: oss-cn-beijing.aliyuncs.com
      keyid: xxxxxxxxxxxxx
      keysecret: xxxxxxxxxxxxx
      filehost: avatar
  1. 服务中创建constant包

  2. constant中创建常量配置类

@Component
public class OSSConstant implements InitializingBean {

    @Value("${aliyun.oss.file.bucketname}")
    private String bucketName;

    @Value("${aliyun.oss.file.endpoint}")
    private String endpoint;

    @Value("${aliyun.oss.file.keyid}")
    private String keyId;

    @Value("${aliyun.oss.file.keysecret}")
    private String keySecret;

    @Value("${aliyun.oss.file.filehost}")
    private String fileHost;

    public static String BUCKET_NAME;

    public static String ENDPOINT;

    public static String ACCESS_KEY_ID;

    public static String ACCESS_KEY_SECRET;

    public static String FILE_HOST;

    // 读取配置文件后做的系统配置,将配置文件的内容赋值给静态常量
    @Override
    public void afterPropertiesSet() throws Exception {
        BUCKET_NAME = this.bucketName;
        ENDPOINT = this.endpoint;
        ACCESS_KEY_ID = this.keyId;
        ACCESS_KEY_SECRET = this.keySecret;
        FILE_HOST = this.fileHost;
    }
}

5. 实现头像上传

OSS文档:help.aliyun.com/document_de…

image.png

5.1 UserController.java

注意,此处上传文件,文件参数前添加的注解为@RequestParam("avatar"),为什么不是@RequestBody?

以为使用POST上传文件时,只能使用POST方式中的form-Data来上传,不能使用POST方式中的Body(Json)来上传。

@ApiOperation(value = "上传用户头像")
@PostMapping("uploadAvatar")
public Response uploadAvatar(
   @ApiParam(name = "avatar", value = "用户头像文件", required = true)
   @RequestParam("avatar") MultipartFile avatar,
   @RequestHeader("Authorization") String token) {
   // 从Token中获取用户id
   DecodedJWT decodedJWT = JWTUtils.verifyToken(token);
   String id = decodedJWT.getClaim("id").asString();

   try {
       String avatarUrl = userService.uploadAvatar(id, avatar);
       return Response.ok().message("上传头像成功").data("avatarUrl", avatarUrl);
   } catch (IOException e) {
       throw new AvatarUploadException(ResponseEnum.AVATAR_UPLOAD_ERROR);
    // return Response.error(ResponseEnum.AVATAR_UPLOAD_ERROR);
   }
}

5.2 UserService.java

/**
* OSS上传头像
* @param avatar 头像文件
* @param id 用户id
* @return 头像文件在bucket中中的绝对路径
*/
String uploadAvatar(String id, MultipartFile avatar) throws IOException;

/**
* OSS删除头像
* @param bucketName bucketName
* @param objectName 头像文件在bucket中的相对路径
*/
void deleteAvatar(String bucketName, String objectName);

5.3 UserServiceImpl.java

@Override
public String uploadAvatar(String id, MultipartFile avatar) throws IOException {

   // 初始化oss参数
   String bucketName = OSSConstant.BUCKET_NAME;
   String endpoint = OSSConstant.ENDPOINT;
   String accessKeyId = OSSConstant.ACCESS_KEY_ID;
   String accessKeySecret = OSSConstant.ACCESS_KEY_SECRET;
   String fileHost = OSSConstant.FILE_HOST;

   // 头像文件在网络中的地址(服务器上的绝对地址)
   String avatarUrl = "";

   // 创建OSSClient实例
   OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

   // 判断该bucket是否存在
   if (!ossClient.doesBucketExist(bucketName)) {
       // 使用aliyun接口创建bucket
       ossClient.createBucket(bucketName);
       // 设置bucket权限:公共读
       ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
   }

   // 获取上传文件流
   InputStream inputStream = avatar.getInputStream();

   // 原始头像文件名
   String originalAvatarName = avatar.getOriginalFilename();

   // 头像文件类型
   String avatarType = originalAvatarName.substring(originalAvatarName.lastIndexOf(".") + 1);

   // 头像上传至bucket的文件名
   String avatarName = UUID.randomUUID().toString() + avatarType;

   // 头像文件在bucket上的相对路径(使用jodatime添加三级目录,每一天更新一个文件夹,提高运行效率)
   String avatarRelativePath = fileHost + "/" + new DateTime().toString("yyyy/MM/dd") + "/" + avatarName;

   // 上传文件至bucket
   ossClient.putObject(bucketName, avatarRelativePath, inputStream);

   // 关闭OSSClient
   ossClient.shutdown();

   // 组装头像文件在bucket上的绝对路径: https://bucketName + . + endpoint/avatarRelativeUrl
   avatarUrl = "https://" + bucketName + "." + endpoint + "/" + avatarRelativePath;

   User user = userMapper.selectById(id);

   // 如果不是第一次上传头像,在bucket中删除原头像文件
   if (user.getAvatarRelativePath() != null) {
       deleteAvatar(bucketName, user.getAvatarRelativePath());
   }

   // 将新avatar的绝对路径和相对路径更新进数据库
   user.setAvatar(avatarUrl);
   user.setAvatarRelativePath(avatarRelativePath);

   UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
   updateWrapper.eq("id", id);
   userMapper.update(user, updateWrapper);

   return avatarUrl;
}

@Override
public void deleteAvatar(String bucketName, String objectName) {
   String endpoint = OSSConstant.ENDPOINT;
   String accessKeyId = OSSConstant.ACCESS_KEY_ID;
   String accessKeySecret = OSSConstant.ACCESS_KEY_SECRET;

   OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

   ossClient.deleteObject(bucketName, objectName);

   ossClient.shutdown();
}