SpringBoot配置腾讯云的COS对象存储

1、引入依赖

<dependency>
    <groupId>com.tencent.cloud</groupId>
    <artifactId>cos-sts-java</artifactId>
    <version>3.0.8</version>
</dependency>

2、配置application.yml

cos:
  appId: XXXXXXXXXXXXXXX
  #腾讯云的SecretId
  secretId: XXXXXXXXXXXXXXXXXXXXXXXXXX
  #腾讯云的SecretId
  secretKey: XXXXXXXXXXXXXXXXXXXXXX
  #腾讯云的bucket (存储桶)
  bucket: XXXXXXXXX
  #腾讯云的region(bucket所在地区)
  region: ap-shanghai
  #腾讯云的临时密钥时长(单位秒)
  durationSeconds: 1800
  #腾讯云的访问基础链接:
  baseUrl: https://XXXXXXXXXXXXXXXX.cos.ap-shanghai.myqcloud.com
  filePathPrefix: dev
#  filePathPrefix: prod

3、COS工具类

 
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import com.XXXXXXXXXXXXXXXXXXXXXXXXXXX.DateUtils;
import com.tencent.cloud.CosStsClient;
import org.json.JSONObject;
import org.springframework.web.multipart.MultipartFile;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * 腾讯云 cos工具类
 */
public class CosUtil {

    //腾讯云的SecretId
    private static String secretId;
    //腾讯云的SecretKey
    private static String secretKey;
    //腾讯云的bucket (存储桶)
    private static String bucket;
    //腾讯云的region(bucket所在地区)
    private static String region;
    //腾讯云的allowPrefix(允许上传的路径)
    private static String allowPrefix;
    //腾讯云的临时密钥时长(单位秒)
    private static String durationSeconds;
    //腾讯云的访问基础链接:
    private static String baseUrl;
    //文件上传前缀路径
    private static String filePathPrefix;

    //初始化
    static {
        Yaml yaml = new Yaml();
        //文件路径是相对类目录(src/main/java)的相对路径
        InputStream in = CosUtil.class.getClassLoader().getResourceAsStream("application.yml");//或者app.yaml
        Map<String, Object> map = yaml.loadAs(in, Map.class);
        secretId = ((Map<String, Object>) map.get("cos")).get("secretId").toString();
        secretKey = ((Map<String, Object>) map.get("cos")).get("secretKey").toString();
        bucket = ((Map<String, Object>) map.get("cos")).get("bucket").toString();
        region = ((Map<String, Object>) map.get("cos")).get("region").toString();
        durationSeconds = ((Map<String, Object>) map.get("cos")).get("durationSeconds").toString();
        baseUrl = ((Map<String, Object>) map.get("cos")).get("baseUrl").toString();
        filePathPrefix = ((Map<String, Object>) map.get("cos")).get("filePathPrefix").toString();
        allowPrefix = "/*";
    }

    /**
     * 上传文件 使用临时密钥上传
     *
     * @param file
     * @return 成功返回文件路径, 失败返回null
     */
    public static String uploadFile(File file) {

        //获取最后一个.的位置
        int lastIndexOf = file.getName().lastIndexOf(".");
        //获取文件的后缀名 .jpg
        String suffix = file.getName().substring(lastIndexOf);

        String filePath = filePathPrefix + "/" + DateUtils.getYear() + "/" + DateUtils.getMonth() + "/";
        String fileName = UUID.randomUUID() + suffix;
        filePath = filePath.startsWith("/") ? filePath : "/" + filePath + fileName;

        //获取临时密钥
        JSONObject temp = getTempKey();
        // 用户基本信息:解析临时密钥中的相关信息
        String tmpSecretId = temp.getJSONObject("credentials").getString("tmpSecretId");
        String tmpSecretKey = temp.getJSONObject("credentials").getString("tmpSecretKey");
        String sessionToken = temp.getJSONObject("credentials").getString("sessionToken");

        // 1 初始化用户身份信息(secretId, secretKey)
        COSCredentials cred = new BasicCOSCredentials(tmpSecretId, tmpSecretKey);
        // 2 设置 bucket 区域
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        // 3 生成 cos 客户端
        COSClient cosclient = new COSClient(cred, clientConfig);

        // 上传 object, 建议 20M 以下的文件使用该接口
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, filePath, file);

        // 设置 x-cos-security-token header 字段
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setSecurityToken(sessionToken);
        putObjectRequest.setMetadata(objectMetadata);
        String rtValue = null;
        try {
            PutObjectResult putObjectResult = cosclient.putObject(putObjectRequest);
            // 成功:putobjectResult 会返回文件的 etag
            String etag = putObjectResult.getETag();
            rtValue = baseUrl + filePath;
        } catch (CosServiceException e) {
            //失败,抛出 CosServiceException
            e.printStackTrace();
        } catch (CosClientException e) {
            //失败,抛出 CosClientException
            e.printStackTrace();
        } finally {
            // 关闭客户端
            cosclient.shutdown();
            //返回文件的网络访问url
            System.err.println(rtValue);
            return rtValue;
        }
    }

    public static String uploadMultipartFile(File file, MultipartFile multipartFile) {
        UUID id = UUID.randomUUID();
        String[] idd = id.toString().split("-");
        String str = idd[0] + idd[1] + idd[2];
        String filePath = filePathPrefix + "/" + DateUtils.getYear() + "/" + DateUtils.getMonth() + "/";
        filePath = filePath.startsWith("/") ? filePath : "/" + filePath + str + multipartFile.getOriginalFilename();

        //获取临时密钥
        JSONObject temp = getTempKey();
        // 用户基本信息:解析临时密钥中的相关信息
        String tmpSecretId = temp.getJSONObject("credentials").getString("tmpSecretId");
        String tmpSecretKey = temp.getJSONObject("credentials").getString("tmpSecretKey");
        String sessionToken = temp.getJSONObject("credentials").getString("sessionToken");

        // 1 初始化用户身份信息(secretId, secretKey)
        COSCredentials cred = new BasicCOSCredentials(tmpSecretId, tmpSecretKey);
        // 2 设置 bucket 区域
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        // 3 生成 cos 客户端
        COSClient cosclient = new COSClient(cred, clientConfig);

        // 上传 object, 建议 20M 以下的文件使用该接口
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, filePath, file);

        // 设置 x-cos-security-token header 字段
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setSecurityToken(sessionToken);
        putObjectRequest.setMetadata(objectMetadata);
        String rtValue = null;
        try {
            PutObjectResult putObjectResult = cosclient.putObject(putObjectRequest);
            // 成功:putobjectResult 会返回文件的 etag
            String etag = putObjectResult.getETag();
            rtValue = baseUrl + filePath;
        } catch (CosServiceException e) {
            //失败,抛出 CosServiceException
            e.printStackTrace();
        } catch (CosClientException e) {
            //失败,抛出 CosClientException
            e.printStackTrace();
        } finally {
            // 关闭客户端
            cosclient.shutdown();
            //返回文件的网络访问url
            System.err.println(rtValue);
            return rtValue;
        }
    }

    /**
     * 生成临时密钥
     * <p>
     * 官网demo:https://cloud.tencent.com/document/product/436/14048
     *
     * @return
     */
    private static JSONObject getTempKey() {
        JSONObject credential = new JSONObject();

        TreeMap<String, Object> config = new TreeMap<String, Object>();

        try {
            // 替换为您的 SecretId
            config.put("SecretId", secretId);
            // 替换为您的 SecretKey
            config.put("SecretKey", secretKey);

            // 临时密钥有效时长,单位是秒,默认1800秒,最长可设定有效期为7200秒
            config.put("durationSeconds", Integer.parseInt(durationSeconds));

            // 换成您的 bucket
            config.put("bucket", bucket);
            // 换成 bucket 所在地区
            config.put("region", region);

            // 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子:a.jpg 或者 a/* 或者 * 。
            // 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。
            config.put("allowPrefix", allowPrefix);

            // 密钥的权限列表。简单上传、表单上传和分片上传需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            String[] allowActions = new String[]{
                    // 简单上传
                    "name/cos:PutObject",
                    // 表单上传、小程序上传
                    "name/cos:PostObject",
                    // 分片上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload"
            };
            config.put("allowActions", allowActions);

            credential = CosStsClient.getCredential(config);
            //成功返回临时密钥信息,如下打印密钥信息
            //LogManager.debug(TAG, credential.toString());
            System.out.println(credential);
        } catch (Exception e) {
            //失败抛出异常
            throw new IllegalArgumentException("no valid secret !");
        }

        return credential;
    }

    /**
     * 用缓冲区来实现这个转换, 即创建临时文件
     * 使用 MultipartFile.transferTo()
     *
     * @param multipartFile
     * @return
     */
    public static File transferToFile(MultipartFile multipartFile) throws IOException {
        String originalFilename = multipartFile.getOriginalFilename();
        String prefix = originalFilename.split("\\.")[0];
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        File file = File.createTempFile(prefix, suffix);
        multipartFile.transferTo(file);
        return file;
    }

    public static void deleteFile(String fileName) {
        COSClient cosClient = null;
        try {
            // 1 初始化用户身份信息(secretId, secretKey)。
            COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
            // 2 设置bucket的区域, COS地域的简称请参照
            // https://cloud.tencent.com/document/product/436/6224
            // clientConfig中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者接口文档 FAQ中说明。
            ClientConfig clientConfig = new ClientConfig(new Region(region));
            // 3 生成 cos 客户端。
            cosClient = new COSClient(cred, clientConfig);
            cosClient.deleteObject(bucket, fileName);
        } finally {
            if (cosClient != null) {
                // 关闭客户端(关闭后台线程)
                cosClient.shutdown();
            }
        }
    }

    /**
     * 上传测试
     **/
    public static void main(String[] args) {
        //创建文件
        File file = new File("D:\\微信图片_20220317095722.png");

        //第一种:固定密钥
        /**
         * 如果filePath是空或者是null,就以当前日期的形式存放  如:yyyy/yyyymmdd/xxx.文件后缀
         * 如果指定文件夹:如 ttt/
         *                  ps:要加斜线 /
         *                  表示存放在目录下的ttt文件夹下
         **/
        uploadFile(file); 

    }

}

4、Controller使用COS的工具类

 

import com.common.core.domain.ReturnResult;
import com.utils.CosUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@RestController
@RequestMapping("/cos/cos")
@Api(value = "/cos/cos", description = "腾讯云文件操作")
public class CosController {
    /**
     * 腾讯云上传对象
     *
     * @param multipartFile
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "腾讯云上传对象")
    @PreAuthorize("@ss.hasPermi('file:file:uploadFile')")
    @PostMapping(value = "/uploadFile")
    public ReturnResult uploadFile(@RequestParam MultipartFile multipartFile, String... flag) {
        File file = null;
        try {
            file = CosUtil.transferToFile(multipartFile);
            if (flag != null) {
                String filePath = CosUtil.uploadMultipartFile(file, multipartFile);
                return ReturnResult.success(filePath);
            } else {
                String filePath = CosUtil.uploadFile(file);
                return ReturnResult.success(filePath);
            }

        } catch (IOException e) {
            return ReturnResult.error(e.getMessage());
        }
    }

    /**
     * 腾讯云删除对象
     *
     * @param fileName
     * @return
     */
    @ApiOperation(value = "腾讯云删除对象")
    @PreAuthorize("@ss.hasPermi('file:file:deleteFile')")
    @GetMapping("/deleteFile")
    @PostMapping(value = "/deleteFile")
    public ReturnResult deleteFile(@RequestParam String fileName) {
        CosUtil.deleteFile(fileName);
        return ReturnResult.success();
    }

}

 5、前端上传文件到COS的组件

<template>
  <div class="upload-file">
    <el-upload
      :action="uploadFileUrl"
      :before-upload="handleBeforeUpload"
      v-model:file-list="fileList"
      :limit="limit"
      :data="{flag:true}"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :on-success="handleUploadSuccess"
      :show-file-list="false"
      name="multipartFile"
      :headers="headers"
      :multiple="true"
      class="upload-file-uploader"
      ref="upload"
    >
      <!-- 上传按钮 -->
      <el-button :type="isBackground ? 'primary' : 'text'" >上传文件</el-button>
      <!-- 上传提示 -->
      <div class="el-upload__tip" v-if="showTip">
        请上传
        <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b><br/></template>
        <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
        的文件
      </div>
    </el-upload>

    <!-- 文件列表 -->
    <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
      <template v-for="(file, index) in fileList" :key="file.uid">
        <li  class="el-upload-list__item ele-upload-list__item-content" v-if="file.url">
          <el-link :href="file.url" :underline="false"  target="_blank">
            <span class="el-icon-document"> {{ file.name}} </span>
          </el-link>
          <div>
            <el-button link :href="file.url" :underline="false" @click="downLoad(file)" type="primary">下载</el-button>
            <el-button link :underline="false" @click="handleDelete(index)" type="danger">删除</el-button>
          </div>
        </li>
      </template>
    </transition-group>
  </div>
</template>

<script setup>
import { getToken } from "@/utils/auth";
import { saveAs } from 'file-saver'
const props = defineProps({
  modelValue: [String, Object, Array],
  // 数量限制
  limit: {
    type: Number,
    default: 5,
  },
  // 大小限制(MB)
  fileSize: {
    type: Number,
    default: 20,
  },
  // 文件类型, 例如['png', 'jpg', 'jpeg']
  fileType: {
    type: Array,
    default: () => ["doc", "xls", "ppt", "txt", "pdf","docx",'xlsx',"jpg",'jpge',"png"],
  },
  // 是否显示提示
  isShowTip: {
    type: Boolean,
    default: true
  },
  //是否显示背景边框
  isBackground:{
    type:Boolean,
    default:true
  },
});

const { proxy } = getCurrentInstance();
const emit = defineEmits();
const baseUrl = 'https://XXXXXXXXXXXXXXXXXXX.cos.ap-shanghai.myqcloud.com';
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/cos/cos/uploadFile"); // 上传的图片服务器地址
const headers = ref({ Authorization: "Bearer " + getToken() });
const fileList = ref([]);
const showTip = computed(
  () => props.isShowTip && (props.fileType || props.fileSize)
);


watch(() => props.modelValue, val => {
  if (val) {
    let temp = 1;
    // 首先将值转为数组
    const list = Array.isArray(val) ? val : props.modelValue.split(',');
    // 然后将数组转为对象数组
    fileList.value = list.map(item => {
      if (typeof item === "string") {
        item = { name: getFileName(item), url: item };
      }
      item.uid = item.uid || new Date().getTime() + temp++;
      return item;
    });
  } else {
    fileList.value = [];
    return [];
  }
});

// 上传前校检格式和大小
function handleBeforeUpload(file) {
  // 校检文件类型
  if (props.fileType.length) {
    let fileExtension = "";
    if (file.name.lastIndexOf(".") > -1) {
      fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
    }
    const isTypeOk = props.fileType.some((type) => {
      if (file.type.indexOf(type) > -1) return true;
      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
      return false;
    });
    if (!isTypeOk) {
      proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
      return false;
    }
  }
  // 校检文件大小
  if (props.fileSize) {
    const isLt = file.size / 1024 / 1024 < props.fileSize;
    if (!isLt) {
      proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
      return false;
    }
  }
  return true;
}

// 文件个数超出
function handleExceed() {
  proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}

// 上传失败
function handleUploadError(err) {
  proxy.$modal.msgError("上传失败");
}

// 上传成功回调
function handleUploadSuccess(res, file) {
  if(res.code!==200){
     proxy.$modal.msgError(res.msg);
     const index = fileList.value.findIndex(item=>item.uid===file.uid)
     handleDelete(index)
     return
  }
  proxy.$modal.msgSuccess("上传成功");
  // fileList.value.push({ name: res.fileName, url: res.fileName,file});
  console.log(fileList.value)
  console.log(file)
  const cFile =  fileList.value.find(item=>item.uid===file.uid)
  cFile.url = res.data;
  // console.log(fileList.value)
  if(fileList.value.every(item=>item.status==='success')){
    emit("update:modelValue", listToString(fileList.value));
  }
}

// 删除文件
function handleDelete(index) {
  fileList.value.splice(index, 1);
  emit("update:modelValue", listToString(fileList.value));
}

// 获取文件名称
function getFileName(name) {
  if (name.lastIndexOf("/") > -1) {
    const prefix = 16
    return name.slice(name.lastIndexOf("/") + prefix+1).toLowerCase();
  } else {
    return "";
  }
}
//下载
function downLoad(file){
  saveAs(file.url, file.name, {});
}
// 对象转成指定字符串分隔
function listToString(list, separator) {
  let strs = "";
  separator = separator || ",";
  for (let i in list) {
    strs += list[i].url + separator;
  }
  return strs !== '' ? strs.substr(0, strs.length - 1) : '';
}
</script>

<style scoped lang="scss">
.upload-file-uploader {
  margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
  border: 1px solid #e4e7ed;
  line-height: 2;
  margin-bottom: 10px;
  position: relative;
}
.upload-file-list .ele-upload-list__item-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: inherit;
}
.ele-upload-list__item-content-action .el-link {
  margin-right: 10px;
}
.el-upload__tip{
  margin-left: 10px;
}
</style>

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ahwangzc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值