package com.backendsys.service.SDKService.SDKTencent; import cn.hutool.core.io.FileUtil; import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import com.backendsys.aspect.HttpRequestAspect; import com.backendsys.entity.SDKEntity.TencentCOSCredentialsDTO; import com.backendsys.entity.System.SysFile.SysFileCategoryDTO; import com.backendsys.entity.System.SysFile.SysFileDTO; import com.backendsys.entity.Tencent.TencentCos.MultipartUploadRespDTO; import com.backendsys.entity.Tencent.TencentCos.MultipartUploadDTO; import com.backendsys.entity.Tencent.TencentCos.TempCredentialsDTO; import com.backendsys.entity.Tencent.TencentCos.UploadOriginDTO; import com.backendsys.entity.Tencent.TencentCos.UploadOriginResp; import com.backendsys.exception.CustException; //import com.backendsys.mapper.System.SysFileCategoryMapper; //import com.backendsys.mapper.System.SysFileMapper; //import com.backendsys.service.System.SysFileService; import com.backendsys.utils.CommonUtil; import com.backendsys.utils.MapUtil; 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.CosServiceException; import com.qcloud.cos.model.*; import com.qcloud.cos.region.Region; import com.qcloud.cos.transfer.TransferManager; import com.qcloud.cos.utils.IOUtils; import com.tencent.cloud.CosStsClient; import com.tencent.cloud.Response; import net.coobird.thumbnailator.Thumbnails; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.UriComponentsBuilder; import java.io.*; import java.net.*; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Matcher; import java.util.regex.Pattern; @Service public class SDKTencentCOSServiceImpl implements SDKTencentCOSService { @Autowired private HttpRequestAspect httpRequestAspect; @Value("${tencent.cos.secret-id-temp}") private String secretIdTemp; @Value("${tencent.cos.secret-key}-temp") private String secretKeyTemp; @Value("${tencent.cos.secret-id}") private String secretId; @Value("${tencent.cos.secret-key}") private String secretKey; @Value("${tencent.cos.region}") private String region; @Value("${tencent.cos.bucket-name}") private String bucketName; @Value("${tencent.cos.accessible-domain}") private String accessibleDomain; @Value("${tencent.cos.max-size}") private long maxSize; @Autowired private SDKTencentService sdkTencentService; // @Autowired // private SysFileMapper sysFileMapper; // @Autowired // private SysFileService sysFileService; // // @Autowired // private SysFileCategoryMapper sysFileCategoryMapper; /** * 生成cos客户端 * COSClient cosClient = getClient(secretId, secretKey); */ private COSClient getClient(String secretId, String secretKey) { // 初始化用户身份信息(secretId, secretKey) COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); // 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224 Region region1 = new Region(region); ClientConfig clientConfig = new ClientConfig(region1); clientConfig.setRegion(region1); // 生成cos客户端 COSClient cosClient = new COSClient(cred, clientConfig); return cosClient; } // /** // * 临时密钥生成及使用指引 // * https://www.tencentcloud.com/zh/document/product/436/14048 // */ // @Override // public TempCredentialsDTO getTempCredentials(String allowPrefix) { // // TreeMap KLingConfig = new TreeMap(); // try { // // // 密钥,使用子账号密钥,授权遵循最小权限指引 // KLingConfig.put("secretId", secretId); // KLingConfig.put("secretKey", secretKey); // // // 临时密钥有效时长,单位是秒,默认 120 秒,目前主账号最长 2 小时(即 7200 秒),子账号最长 36 小时(即 129600)秒 // Integer durationSeconds = 120; // KLingConfig.put("durationSeconds", durationSeconds); // KLingConfig.put("bucket", bucketName); // KLingConfig.put("region", region); // // // 获取当前时间戳(毫秒) // long nowDateTime = System.currentTimeMillis() / 1000L; // // 计算1800秒后的时间戳 // long expireDateTime = nowDateTime + durationSeconds; // // // 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子:a.jpg 或者 a/* 或者 * 。 // // 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。 // KLingConfig.put("allowPrefix", allowPrefix); // // // 密钥的权限列表。简单上传、表单上传和分片上传需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923 // String[] allowActions = new String[] { // //简单上传操作 // "name/cos:PutObject", // //表单上传对象 // "name/cos:PostObject", // //分块上传:初始化分块操作 // "name/cos:InitiateMultipartUpload", // //分块上传:List 进行中的分块上传 // "name/cos:ListMultipartUploads", // //分块上传:List 已上传分块操作 // "name/cos:ListParts", // //分块上传:上传分块操作 // "name/cos:UploadPart", // //分块上传:完成所有分块上传操作 // "name/cos:CompleteMultipartUpload", // //取消分块上传操作 // "name/cos:AbortMultipartUpload" // }; // // "name/cos:*" // // KLingConfig.put("allowActions", allowActions); // // // -- 在后台 存储桶->设置策略 ------------------------------------------------------------- // // // 限制上传文件的大小(cos:content-length) // // 限制上传文件的类型(cos:content-type) // // https://cloud.tencent.com/document/product/436/71307#content-length // // // // Response response = CosStsClient.getCredential(KLingConfig); // // TempCredentialsDTO dto = new TempCredentialsDTO(); // dto.setRegion(region); // dto.setBucket(bucketName); // dto.setAllow_prefix(allowPrefix.replace("*", "")); // dto.setTmp_secret_id(response.credentials.tmpSecretId); // dto.setTmp_secret_key(response.credentials.tmpSecretKey); // dto.setSession_token(response.credentials.sessionToken); // dto.setStart_time(nowDateTime); // dto.setExpired_time(expireDateTime); // dto.setDuration_seconds(durationSeconds); // // return dto; // // } catch (Exception e) { // e.printStackTrace(); // throw new CustException(e.getMessage()); // } // // } // /** // * [储存桶操作] 获得当前储存桶的文件列表 // * https://cloud.tencent.com/document/product/436/65938 // */ // @Override // public Map getBucketList(String next_marker) { // COSClient cosClient = getClient(secretId, secretKey); // // ListObjectsRequest listObjectsRequest = new ListObjectsRequest(); // // 设置 bucket 名称 // listObjectsRequest.setBucketName(bucketName); // // // // // // // 设置列出的对象名以 prefix 为前缀 // listObjectsRequest.setPrefix("/materials/1/"); //// listObjectsRequest.setPrefix(""); // // // // // // // 设置最大列出多少个对象, 一次 listobject 最大支持1000 // listObjectsRequest.setMaxKeys(5); // // // // 设置被截断开始的位置 // if (next_marker != null) { // listObjectsRequest.setMarker(next_marker); // } // // // 保存列出的结果 // ObjectListing objectListing = null; // // try { // objectListing = cosClient.listObjects(listObjectsRequest); // System.out.println(objectListing); // // } catch (CosServiceException e) { // e.printStackTrace(); // } // // // object summary 表示此次列出的对象列表 // List cosObjectSummaries = objectListing.getObjectSummaries(); // System.out.println(cosObjectSummaries); // // List> list = new ArrayList<>(); // // for (COSObjectSummary cosObjectSummary : cosObjectSummaries) { // // // 对象的 key // String key = cosObjectSummary.getKey(); // // 对象的 etag // String etag = cosObjectSummary.getETag(); // // 对象的长度 // long fileSize = cosObjectSummary.getSize(); // // 对象的存储类型 // String storageClasses = cosObjectSummary.getStorageClass(); // // System.out.println("key = " + key); // System.out.println("etag = " + etag); // System.out.println("fileSize = " + fileSize); // System.out.println("storageClasses = " + storageClasses); // System.out.println("------------------------------------------"); // // Map map = new LinkedHashMap<>(); // map.put("key", cosObjectSummary.getKey()); // map.put("etag", cosObjectSummary.getETag()); // map.put("file_size", cosObjectSummary.getSize()); // map.put("storage_classes", cosObjectSummary.getStorageClass()); // list.add(map); // // } // // String nextMarker = null; // if (objectListing.isTruncated()) { // // 表示还没有列完,被截断了 // // 下一次开始的位置 // nextMarker = objectListing.getNextMarker(); // System.out.println("isTruncated! nextMarker:"); // System.out.println(nextMarker); // } // // Map result = new LinkedHashMap<>(); // result.put("next_marker", nextMarker); // result.put("list", list); // // return result; // } /** * 在调用 [AI数智人-定制接口] 前,需要将素材文件上传到指定的云端存储。 * - 通过接口获取上传临时令牌,接口返回令牌后,由前端上传对应的文件到对象存储 */ /** * 文档中心 > 腾讯云智能数智人 > API 接入 > 2D真人小样本形象定制 API 文档-v0.2.1 > 上传素材文件到云端存储 > 获取上传临时令牌 * https://cloud.tencent.com/document/product/1240/96067 */ /** * 获取上传临时令牌 (返回远程参数) */ private Map getIvhTempCredentialsOrigin(String appKey, String accessToken) { // 获得 Signature 签名 Map signature = sdkTencentService.getSignature(appKey, accessToken); // 构建带参数的 URI (注意参数不要编码) (signature 由于会带+号所以需要改成这种写法) String url = "https://gw.tvs.qq.com/v2/ivh/assetmanager/customservice/getuploadcredentials"; URI uriWithParams = UriComponentsBuilder.fromUriString(url) .queryParam("appkey", signature.get("appkey")) .queryParam("signature", "{signature}") .queryParam("timestamp", signature.get("timestamp")) .build(signature.get("signature")); String jsonStr = "{\"Payload\": {}}"; JSONObject bodyData = new JSONObject(jsonStr); // Http 远程调用 Map resp = WebClient.create() .method(HttpMethod.POST) .uri(uriWithParams) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8") .bodyValue(bodyData) .retrieve() .bodyToMono(new ParameterizedTypeReference>() { }) .block(); /* resp: { "Header": { "RequestID": "gze0f59a6c17104906483254895", "SessionID": "gze0f59a6c17104906483254896", "DialogID": "", "Code": 0, "Message": "ok" }, "Payload": { "ExpiredTime": 1710494248, "PathPrefix": "https://virtualhuman-cos-prod-1251316161.cos.accelerate.myqcloud.com/customer-pipeline/884/70c53bfe-c0e1-481a-8ae3-4e76f03d2971/", "StartTime": 1710490648, "Credentials": { "Token": "owlVQ..", "TmpSecretId": "AKIDxTiqWc9i0bSsPJy2uduUCJOTxVvMYxrWiJTS0V4OtTEm_UQqiq-ECwf38Xtrtl44", "TmpSecretKey": "zmsLEogWD0lAY/SWHnjH8XfzrDs0Vicfd4RGECehBCE=" } } } */ return resp; } /** * 获取上传临时令牌 (返回格式化后的参数) */ public TencentCOSCredentialsDTO getIvhTempCredentials(String appKey, String accessToken) { Map resp = getIvhTempCredentialsOrigin(appKey, accessToken); // 判断是否获取成功 Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code"); if (respHeaderCode != 0) { String respHeaderMessage = (String) MapUtil.get(resp, "Header.Message"); throw new RuntimeException(respHeaderMessage); } TencentCOSCredentialsDTO credentialsDTO = new TencentCOSCredentialsDTO(); credentialsDTO.setStart_time((Integer) MapUtil.get(resp, "Payload.StartTime")); credentialsDTO.setExpired_time((Integer) MapUtil.get(resp, "Payload.ExpiredTime")); credentialsDTO.setTmp_secret_id((String) MapUtil.get(resp, "Payload.Credentials.TmpSecretId")); credentialsDTO.setTmp_secret_key((String) MapUtil.get(resp, "Payload.Credentials.TmpSecretKey")); credentialsDTO.setSession_token((String) MapUtil.get(resp, "Payload.Credentials.Token")); // 通过 PathPrefix 获得 bucket 和 region String pathPrefix = (String) MapUtil.get(resp, "Payload.PathPrefix"); Pattern pattern = Pattern.compile("https://([a-zA-Z0-9\\-]+)\\.cos\\.(\\w+)\\.myqcloud\\.com/.*"); Matcher matcher = pattern.matcher(pathPrefix); if (matcher.find()) { credentialsDTO.setBucket(matcher.group(1)); credentialsDTO.setRegion(matcher.group(2)); } credentialsDTO.setPath_prefix(pathPrefix); int startIndex = pathPrefix.indexOf("customer-pipeline"); String lastPath = pathPrefix.substring(startIndex); credentialsDTO.setKey_dir(lastPath); /* credentialsDTO: { "bucket": "virtualhuman-cos-prod-1251316161", "region": "accelerate", "path_prefix": "https://virtualhuman-cos-prod-1251316161.cos.accelerate.myqcloud.com/customer-pipeline/884/3e52ec21-02ea-403c-8d1c-ebb727ceac1f/", "key_dir": "customer-pipeline/884/3e52ec21-02ea-403c-8d1c-ebb727ceac1f/", "tmp_secret_id": "AKIDqv8LDYIWVdqq5PrNpGD9J4YY0p8QQ7qXe2ZNV78W_y9wXFGXe6z4wkRJu0OQ3jhE", "tmp_secret_key": "8TQWL6+YweYQc2yDCNBDe2/V/qdNZYhYczSS6QdUcwk=", "session_token": "czCmJWynL6j78eXE..", "start_time": 1712253511, "expired_time": 1712257111 } */ return credentialsDTO; } /** * 高级操作 */ public TransferManager createTransferManager(COSClient cosClient) { // 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源 // 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。 ExecutorService threadPool = Executors.newFixedThreadPool(32); // 传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。 TransferManager transferManager = new TransferManager(cosClient, threadPool); return transferManager; } public void shutdownTransferManager(TransferManager transferManager) { // 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例。 // 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。 transferManager.shutdownNow(true); } // /** // * 压缩图片 (入参 InputStream,出参 File) // */ // public File compressImage(InputStream inputStream, String suffix) throws IOException { // // System.out.println("suffix: " + suffix); // // // 将文件内容写入临时文件 //// String tempFilename = CommonUtil.generateFilename(suffix); //// String tempFilenameThumb = tempFilename.replaceAll("." + suffix, "-thumb." + suffix); // //// System.out.println("tempFilename: " + tempFilename); //// System.out.println("tempFilenameThumb: " + tempFilenameThumb); // System.out.println("-------------------------------------------"); // // File tempFile = File.createTempFile("tmp", "." + suffix); // File tempFileThumb = File.createTempFile("tmp", "." + suffix); // // System.out.println("tempFile: " + tempFile); // System.out.println("tempFileThumb: " + tempFileThumb); // // try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { // System.out.println("IOUtils.copy start: "); // IOUtils.copy(inputStream, outputStream); // } catch (Exception e) { // System.out.println("IOUtils.copy Error:"); // System.out.println(e); // } // // 创建缩略图 // boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase()); // if (isImage) { // File sourceFile = new File(tempFile.getPath()); // File sourceFileThumb = new File(tempFileThumb.getPath()); // // 使用Thumbnailator生成缩略图并写入到临时文件 // System.out.println("Thumbnails start"); // try { // System.out.println("sourceFile:"); // System.out.println(sourceFile); // System.out.println("sourceFileThumb:"); // System.out.println(sourceFileThumb); // Thumbnails.of(sourceFile).size(60, 60).outputQuality(0.2).toFile(sourceFileThumb); // tempFileThumb.delete(); // } catch (Exception e) { // System.out.println(e); // } // System.out.println("Thumbnails end"); // return sourceFile; // } // return null; // } /** * [小文件] 将 远程文件 异步保存到 COS储存桶 * 注意: * - 由于是异步上传,可能访问会有延迟 * - 如果文件过大,可能会造成内存溢出,所以最好 不要作为上传大文件去使用,不要作为公开接口 */ public UploadOriginResp uploadOrigin(UploadOriginDTO uploadOriginDTO) throws IOException { if (uploadOriginDTO.getUrl() == null) throw new CustException("图片不能为空"); // 获得自身 user_id Long user_id = httpRequestAspect.getUserId(); // 获得远程 url URL url = new URL(uploadOriginDTO.getUrl()); // 获得文件元数据,并判断文件是否失效或过期 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.connect(); int responseCode = connection.getResponseCode(); if (responseCode != HttpURLConnection.HTTP_OK) throw new CustException("文件失效或已过期"); // 获取系统默认的临时目录 Path tempDirPath = Paths.get(System.getProperty("java.io.tmpdir")); // 下载文件到本地 (可能会有性能问题?) HttpUtil.downloadFile(uploadOriginDTO.getUrl(), tempDirPath.toFile()); // 获得 文件名 String filename = Paths.get(url.getPath()).getFileName().toString(); // 获得 后缀名 String suffix = StringUtils.getFilenameExtension(filename); // 判断 是否为图片 boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase()); // 获得 文件名 (缩略图) String filenameThumb = isImage ? filename.replaceAll("." + suffix, "-thumb." + suffix) : null; File downloadFile = new File(tempDirPath + File.separator + filename); File downloadFileThumb = isImage ? new File(File.createTempFile("tmp", "." + suffix).getPath()) : null; if (isImage) { // 压缩图片 Thumbnails.of(downloadFile.getPath()).size(60, 60).outputQuality(0.2).toFile(downloadFileThumb); } // 获得 要保存的路径/文件名 String key = uploadOriginDTO.getDir() + "/" + filename; String keyThumb = isImage ? uploadOriginDTO.getDir() + "/" + filenameThumb : null; String remotePath = accessibleDomain + "/" + key; String remotePathThumb = isImage ? accessibleDomain + "/" + keyThumb : null; // 创建返回值实体类 UploadOriginResp resp = new UploadOriginResp(); resp.setUrl(accessibleDomain + "/" + key); resp.setUrl_thumb(isImage ? accessibleDomain + "/" + keyThumb : null); resp.setOrigin_url(uploadOriginDTO.getUrl()); // 返回文件元数据 (类型,大小,最后更新时间) resp.setContent_type(connection.getContentType()); resp.setContent_length(connection.getContentLength()); resp.setLast_modified(connection.getLastModified()); // -- 创建异步上传任务 --------------------------------------------------------------------- CompletableFuture.runAsync(() -> { try { // -- 将文件流保存到对象储存桶 ------------------------------------------------------ COSClient cosClient = getClient(secretId, secretKey); // // 检查对象是否存在于存储桶中 // if (checkObjectExist(cosClient, key)) { // System.out.println(key + " 已存在,不需要上传"); // } else { // 上传原图 PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, downloadFile); PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); System.out.println("putObjectResult: " + putObjectResult.toString()); // 上传缩略图 if (isImage) { PutObjectRequest putObjectRequestThumb = new PutObjectRequest(bucketName, keyThumb, downloadFileThumb); PutObjectResult putObjectResultThumb = cosClient.putObject(putObjectRequestThumb); System.out.println("putObjectResultThumb: " + putObjectResultThumb.toString()); } // -- [DB] 插入数据 ------------------------------------- SysFileDTO fileDTO = new SysFileDTO(); fileDTO.setUser_id(user_id); fileDTO.setName(filename); fileDTO.setFile_key(key); fileDTO.setFile_remote_path(remotePath); if (isImage) { fileDTO.setFile_remote_path_thumb(remotePathThumb); } fileDTO.setSize(Long.valueOf(connection.getContentLength())); InputStream inputStream = new FileInputStream(downloadFile); String md5 = DigestUtil.md5Hex(inputStream); fileDTO.setMd5(md5); // fileDTO.setCategory_id(getCategoryIdBySuffix(suffix)); // sysFileMapper.insertFile(fileDTO); System.out.println("insert success"); // } } catch (IOException e) { throw new RuntimeException(e); } }); return resp; } // // /** // * 查询分块上传任务 // */ // public List listMultipartUploads(String key){ // // COSClient cosClient = getClient(secretId, secretKey); // // ListMultipartUploadsRequest listMultipartUploadsRequest = new ListMultipartUploadsRequest(bucketName); // // 每次请求最多列出多少个 // listMultipartUploadsRequest.setMaxUploads(100); // // 设置要查询的分块上传任务的目标前缀,直接设置要查询的 key // listMultipartUploadsRequest.setPrefix(key); // // MultipartUploadListing multipartUploadListing = null; // // boolean found = false; // List list = new ArrayList<>(); // do { // multipartUploadListing = cosClient.listMultipartUploads(listMultipartUploadsRequest); // List multipartUploads = multipartUploadListing.getMultipartUploads(); // // for (MultipartUpload mUpload : multipartUploads) { // if (mUpload.getKey().equals(key)) { // System.out.println(mUpload.getUploadId()); // list.add(mUpload.getUploadId()); // //found = true; // //break; // } // } // // //if (found) { // // break; // //} // listMultipartUploadsRequest.setKeyMarker(multipartUploadListing.getNextKeyMarker()); // listMultipartUploadsRequest.setUploadIdMarker(multipartUploadListing.getNextUploadIdMarker()); // } while (multipartUploadListing.isTruncated()); // // if (!found) { // System.out.printf("do not found upload task with key: %s\n", key); // } // // return list; // } // /** // * 简单上传 // */ // public MultipartUploadRespDTO simpleUpload(MultipartFile file) { // // // 检查上传的文件是否为空 // if (file == null || file.isEmpty()) throw new CustException("file 上传文件不能为空"); // // // 判断文件大小是否超过 // if (file.getSize() > maxSize) { // throw new CustException("上传文件不能大于 " + maxSize/1024/1024 + " MB,如有需要请使用大文件上传功能"); // } // // // 生成cos客户端 // COSClient cosClient = getClient(secretId, secretKey); // // MultipartUploadRespDTO resp = new MultipartUploadRespDTO(); // try { // // String md5 = DigestUtil.md5Hex(file.getInputStream()); // String suffix = FileUtil.extName(file.getOriginalFilename()).toLowerCase(); // String filename = md5 + "." + suffix; // String filenameThumb = filename.replaceAll("." + suffix, "-thumb." + suffix); // String key = "temp/" + filename; // String keyThumb = "temp/" + filenameThumb; // String remotePath = accessibleDomain + "/" + key; // String remotePathThumb = accessibleDomain + "/" + keyThumb; // //// // 获得文件分类列表 //// SysFileCategoryDTO categoryDTO = new SysFileCategoryDTO(); //// List categoryList = sysFileCategoryMapper.queryFileCategoryList(categoryDTO); // // // 获得图片分类的后缀 (待做),再进行以下判断 // // // 还有删除也要同时删除缩略图 // // // 还有大文件分片也要做缩略图 // // // 还有 视频要做截图生成 // // // // // // boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase()); // // // // // // // // // 检查对象是否存在于存储桶中 // if (checkObjectExist(cosClient, key)) { // // -- [COS] 对象存在时,直接返回路径 ------------------------------- // resp.setIs_fast_upload(true); // // } else { // // 获取文件内容 // InputStream inputStream = file.getInputStream(); // // // 将文件内容写入临时文件 // String tempFilename = CommonUtil.generateFilename(null); // File tempFile = File.createTempFile(tempFilename, null); // try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { // IOUtils.copy(inputStream, outputStream); // } // // 创建 PutObjectRequest 对象,PutObject 请求。(异步) // PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, tempFile); // cosClient.putObject(putObjectRequest); // // // // 创建缩略图 // if (isImage) { // File tempFileThumb = File.createTempFile(tempFilename, "." + suffix); // String sourceFilePath = tempFileThumb.getPath(); // File sourceFile = new File(sourceFilePath); // // 使用Thumbnailator生成缩略图并写入到临时文件 // Thumbnails.of(tempFile).size(60, 60).outputQuality(0.2).toFile(tempFileThumb); // PutObjectRequest putObjectRequestThumb = new PutObjectRequest(bucketName, keyThumb, sourceFile); // cosClient.putObject(putObjectRequestThumb); // resp.setPath_thumb(remotePathThumb); // tempFileThumb.delete(); // } // // tempFile.delete(); // // } // // // -- [DB] 查询数据 --------------------------------------------- //// SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key); //// // 如果表中没有数据,则添加数据 //// if (fileDetail == null) { // // -- [DB] 插入数据 ----------------------------------------- // SysFileDTO fileDTO = new SysFileDTO(); // fileDTO.setUser_id(httpRequestAspect.getUserId()); // fileDTO.setName(filename); // fileDTO.setFile_key(key); // fileDTO.setFile_remote_path(remotePath); // if (isImage) { // fileDTO.setFile_remote_path_thumb(remotePathThumb); // } //// fileDTO.setCategory_id(getCategoryIdBySuffix(suffix)); // fileDTO.setSize(file.getSize()); // fileDTO.setMd5(md5); // sysFileMapper.insertFile(fileDTO); //// } // // ------------------------------------------------------------- // // resp.setFilename(filename); // resp.setKey(key); // resp.setMd5(md5); // resp.setSize(file.getSize()); // resp.setPath(remotePath); // // } catch (IOException e) { // throw new CustException(e.getMessage()); // } finally { // if (cosClient != null) { // cosClient.shutdown(); // } // } // return resp; // // } // // -- 检查对象是否存在于存储桶中 --------------------------------------------------------------------- // private Boolean checkObjectExist(COSClient cosClient, String key) { // try { // return cosClient.doesObjectExist(bucketName, key); // } catch (CosServiceException e) { // e.printStackTrace(); // } // return false; // } // // -- 初始化分块任务 ------------------------------------------------------------------------------- // private MultipartUploadRespDTO initiateMultipartUpload(COSClient cosClient, MultipartFile file, String key, String filename){ // System.out.println("-- initiateMultipartUpload --"); // try { // // // InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key); // // 分块上传的过程中,仅能通过初始化分块指定文件上传之后的 metadata, 需要的头部可以在这里指定 // ObjectMetadata objectMetadata = new ObjectMetadata(); // request.setObjectMetadata(objectMetadata); // // // InitiateMultipartUploadResult initResult = cosClient.initiateMultipartUpload(request); // // MultipartUploadRespDTO resp = new MultipartUploadRespDTO(); // resp.setUpload_id(initResult.getUploadId()); // resp.setKey(key); // resp.setSize(file.getSize()); // resp.setFilename(filename); // System.out.println("-------------"); // System.out.println(filename); // System.out.println("-------------"); // return resp; // // } catch (CosServiceException e) { // throw new CustException(e.getMessage()); // } // } // // -- 上传分块 ------------------------------------------------------------------------------------ // private MultipartUploadRespDTO uploadPart(COSClient cosClient, MultipartUploadDTO multipartUploadDTO) { // System.out.println("-- uploadPart --"); // try { // InputStream inputStream = multipartUploadDTO.getFile().getInputStream(); // // UploadPartRequest uploadPartRequest = new UploadPartRequest(); // uploadPartRequest.setBucketName(bucketName); // uploadPartRequest.setKey(multipartUploadDTO.getKey()); // uploadPartRequest.setUploadId(multipartUploadDTO.getUpload_id()); // uploadPartRequest.setInputStream(inputStream); // // // 设置分块的长度 (分块文件大小) // uploadPartRequest.setPartSize(multipartUploadDTO.getFile().getSize()); // // 设置要上传的分块编号,从1开始 // uploadPartRequest.setPartNumber(multipartUploadDTO.getIndex()); // // // [COS] 上传分块 // UploadPartResult uploadPartResult = cosClient.uploadPart(uploadPartRequest); // PartETag partETag = uploadPartResult.getPartETag(); // // MultipartUploadRespDTO resp = new MultipartUploadRespDTO(); // resp.setUpload_id(multipartUploadDTO.getUpload_id()); // resp.setKey(multipartUploadDTO.getKey()); // resp.setIndex(partETag.getPartNumber()); // resp.setCount(multipartUploadDTO.getCount()); // resp.setChunk_md5(partETag.getETag()); // resp.setChunk_size(uploadPartRequest.getPartSize()); // return resp; // // } catch (CosServiceException e) { // if ("NoSuchUpload".equals(e.getErrorCode())) { // throw new CustException("找不到相关的 upload_id 或 key 信息,任务已终止或已完成"); // } // throw new CustException(e.getMessage()); // } catch (IOException e) { // throw new RuntimeException(e); // } // } // // -- 查询已上传的分块 ------------------------------------------------------------------------------ // public List listParts(String uploadId, String key) { // COSClient cosClient = getClient(secretId, secretKey); // // // 用于保存已上传的分片信息 // List partETags = new LinkedList<>(); // // PartListing partListing = null; // ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, uploadId); // do { // try { // partListing = cosClient.listParts(listPartsRequest); // } catch (CosServiceException e) { // throw e; // } // for (PartSummary partSummary : partListing.getParts()) { // partETags.add(new PartETag(partSummary.getPartNumber(), partSummary.getETag())); // } // listPartsRequest.setPartNumberMarker(partListing.getNextPartNumberMarker()); // } while (partListing.isTruncated()); // return partETags; // } // // -- 合并分块上传 --------------------------------------------------------------------------------- // private MultipartUploadRespDTO completeMultipartUpload(COSClient cosClient, MultipartUploadDTO multipartUploadDTO) { // System.out.println("-- completeMultipartUpload --"); // // 保存已上传的分片信息,实际情况里,这里的内容是从上传分块接口中获取到的 // List partETags = listParts(multipartUploadDTO.getUpload_id(), multipartUploadDTO.getKey()); // // // 分片上传结束后,调用 complete 完成分片上传 // CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, multipartUploadDTO.getKey(), multipartUploadDTO.getUpload_id(), partETags); // try { // CompleteMultipartUploadResult completeResult = cosClient.completeMultipartUpload(completeMultipartUploadRequest); // // MultipartUploadRespDTO resp = new MultipartUploadRespDTO(); // resp.setRequest_id(completeResult.getRequestId()); // resp.setPath(accessibleDomain + "/" + multipartUploadDTO.getKey()); // return resp; // // } catch (CosServiceException e) { // // // -- [DB] 删除 uploadId 对应的分块信息 --------------------- // sysFileMapper.deleteFile(multipartUploadDTO.getKey()); // // // 终止任务 // abortMultipartUpload(multipartUploadDTO); // // System.out.println("getErrorCode:"); // System.out.println(e.getErrorCode()); // System.out.println(e.getMessage()); // throw new CustException("合并失败,无效的分块,请重新上传", 6001, "(" + e.getErrorCode() + ") " + e.getMessage()); // } // } //// // -- 获得分类 CategoryID (通过文件后缀名) ----------------------------------------------------------- //// private Long getCategoryIdBySuffix(String suffix) { //// SysFileCategoryDTO sysFileCategoryDTO = new SysFileCategoryDTO(); //// List categoryList = sysFileCategoryMapper.queryFileCategoryList(sysFileCategoryDTO); //// Optional firstMatchedId = categoryList.stream().filter(dto -> dto.getSuffix().toLowerCase().contains(suffix)).map(SysFileCategoryDTO::getId).findFirst(); //// Long id = firstMatchedId.orElse(null); //// return id; //// } // // ----------------------------------------------------------------------------------------------- // // /** // * 分块上传 (1判断是否存在,2初始化,3上传分块,4合并分块) // * - https://cloud.tencent.com/document/product/436/65935 // * - https://cloud.tencent.com/document/product/436/14112 // * @param multipartUploadDTO // * @return // */ // public MultipartUploadRespDTO multipartUpload(MultipartUploadDTO multipartUploadDTO) { // // // -- 参数校验 ------------------------------------------------------ // if (multipartUploadDTO.getFile() == null || multipartUploadDTO.getFile().isEmpty()) throw new CustException("file 上传文件不能为空"); // // // 当存在 uploadId 时,即是分块上传阶段 // if (multipartUploadDTO.getUpload_id() != null) { // if (multipartUploadDTO.getKey() == null) throw new CustException("key 不能为空"); // if (multipartUploadDTO.getIndex() == null) throw new CustException("index 不能为空"); // if (multipartUploadDTO.getCount() == null) throw new CustException("count 不能为空"); // if (multipartUploadDTO.getIndex() > multipartUploadDTO.getCount()) { // throw new CustException("index 不能大于 count"); // } // } // // ----------------------------------------------------------------- // // COSClient cosClient = getClient(secretId, secretKey); // // MultipartUploadRespDTO resp = new MultipartUploadRespDTO(); // // // [秒传] 检查对象是否存在,存在即 "秒传" // try { // // String md5 = DigestUtil.md5Hex(multipartUploadDTO.getFile().getInputStream()); // String suffix = FileUtil.extName(multipartUploadDTO.getFile().getOriginalFilename()).toLowerCase(); // String filename = md5 + "." + suffix; // String key = "temp/" + filename; // // // 检查对象是否存在于存储桶中 // if (checkObjectExist(cosClient, key)) { // // String remotePath = accessibleDomain + "/" + key; // // // -- [COS] 对象存在时,直接返回路径 ------------------------------- // resp.setKey(key); // resp.setPath(remotePath); // resp.setIs_fast_upload(true); // // // -- [DB] 查询数据 --------------------------------------------- //// SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key); //// // 如果表中没有数据,则添加数据 //// if (fileDetail == null) { // // -- [DB] 插入数据 ----------------------------------------- // SysFileDTO fileDTO = new SysFileDTO(); // fileDTO.setUser_id(httpRequestAspect.getUserId()); // fileDTO.setName(filename); // fileDTO.setFile_key(key); // fileDTO.setFile_remote_path(remotePath); //// fileDTO.setCategory_id(getCategoryIdBySuffix(suffix)); // // // 获得文件大小 // fileDTO.setSize(CommonUtil.getRemoteFileSize(remotePath)); // // fileDTO.setMd5(md5); // sysFileMapper.insertFile(fileDTO); //// } // // ------------------------------------------------------------- // // } else { // // // 对象不存在时,走创建流程 // String uploadId = multipartUploadDTO.getUpload_id(); // if (uploadId == null || uploadId.isEmpty()) { // // // -- [DB] 查询数据 (By 文件Key) ------------------------------ // SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key); // if (fileDetail != null) { // resp.setUpload_id(fileDetail.getChunk_upload_id()); // resp.setKey(fileDetail.getFile_key()); // resp.setSize(fileDetail.getSize()); // resp.setFilename(filename); //// resp.setIndex(fileDetail.getChunk_current_index()); //// resp.setCount(fileDetail.getChunk_count()); // // 返回 { uploadId, key } // // 缺少 { chunk_md5, chunk_size } // } else { // //// // 上传要求每个分块大小必须大于 1MB,所以整个文件也不能小于 1MB //// if (multipartUploadDTO.getFile().getSize() < 1 * 1024 * 1024) { //// throw new CustException("上传文件不能小于1MB"); //// } // // // -- [初始化] 第一次上传,无 uploadId --------------------- // resp = initiateMultipartUpload(cosClient, multipartUploadDTO.getFile(), key, filename); // // 返回 { uploadId, key } // // // -- [DB] 插入数据 ------------------------------------- // SysFileDTO fileDTO = new SysFileDTO(); // fileDTO.setUser_id(httpRequestAspect.getUserId()); // fileDTO.setName(filename); // fileDTO.setFile_key(key); // fileDTO.setChunk_upload_id(resp.getUpload_id()); // fileDTO.setSize(resp.getSize()); // fileDTO.setMd5(md5); //// fileDTO.setCategory_id(getCategoryIdBySuffix(suffix)); // sysFileMapper.insertFile(fileDTO); // } // // --------------------------------------------------------- // // } else { // // // 分块上传要求每个分块大小必须大于 1MB (最后一个例外) // if (resp.getIndex() != resp.getCount()) { // if (multipartUploadDTO.getFile().getSize() < 1 * 1024 * 1024) { // throw new CustException("分块文件不能小于1MB"); // } // } // // // -- [DB] 查询数据 (By 直传Key) ------------------------------ // SysFileDTO fileDetail = sysFileMapper.queryFileByKey(multipartUploadDTO.getKey()); // // // -- [分块上传] 带着 uploadId 来,开始上传分块 ------------------ // resp = uploadPart(cosClient, multipartUploadDTO); // // 返回 { uploadId, key, index, part_etag, size } // // // -- [合并分块] 当最后一块上传完成时,合并分块 -------------------- // if (resp.getIndex() != null && resp.getCount() != null && resp.getIndex() == resp.getCount()) { // // MultipartUploadRespDTO complete = completeMultipartUpload(cosClient, multipartUploadDTO); // // // 附上最后一次上传的分块信息 // complete.setUpload_id(resp.getUpload_id()); // complete.setKey(resp.getKey()); // complete.setSize(resp.getSize()); // complete.setIndex(resp.getIndex()); // complete.setCount(resp.getCount()); // complete.setChunk_md5(resp.getChunk_md5()); // complete.setChunk_size(resp.getChunk_size()); // // 以及返回 { request_id, path } // resp = complete; // // } // // // if (fileDetail != null) { // // System.out.println("fileDetail != null !"); // // -- [DB] 更新数据 ------------------------------------- // // SysFileDTO fileDTO = new SysFileDTO(); // fileDTO.setUser_id(httpRequestAspect.getUserId()); // fileDTO.setFile_key(resp.getKey()); // // fileDTO.setChunk_current_index(resp.getIndex()); // fileDTO.setChunk_count(resp.getCount()); // // // 没有完成分块时 // if (resp.getPath() != null) { // // Long size = fileDetail.getSize() != null ? fileDetail.getSize() : 0; // fileDTO.setSize(size + resp.getChunk_size()); // // fileDTO.setFile_remote_path(resp.getPath()); // fileDTO.setChunk_current_index(null); // fileDTO.setChunk_count(null); // fileDTO.setChunk_upload_id(""); // } // // sysFileMapper.updateFile(fileDTO); // } // // // ------------------------------------------------------ // } // } // // } catch (IOException e) { // throw new CustException(e.getMessage()); // } finally { // if (cosClient != null) { // cosClient.shutdown(); // } // } // // return resp; // } // // /** // * 终止分块上传任务 // */ // public Map abortMultipartUpload(MultipartUploadDTO multipartUploadDTO){ // // COSClient cosClient = getClient(secretId, secretKey); // // String key = multipartUploadDTO.getKey(); // String uploadId = multipartUploadDTO.getUpload_id(); // // AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, key, uploadId); // try { // cosClient.abortMultipartUpload(abortMultipartUploadRequest); // } catch (CosServiceException e) { // System.out.println("Service"); // System.out.println(e.getErrorCode()); // System.out.println(e.getMessage()); // if ("NoSuchUpload".equals(e.getErrorCode())) { // throw new CustException("找不到相关的 upload_id 或 key 信息,任务已终止或已完成"); // } // throw new CustException(e.getMessage()); // } // // // 确保不再使用 cosClient 之后,关闭即可 // cosClient.shutdown(); // // // -- [DB] 删除数据 -------------------------------------------------- // sysFileMapper.deleteFile(key); // // Map resp = new LinkedHashMap<>(); // resp.put("key", key); // resp.put("upload_id", uploadId); // // return resp; // } // // // // /** // * 删除文件 // */ // public Map removeUploadFile(SysFileDTO sysFileDTO) { // // String name = sysFileDTO.getName(); // String suffix = StringUtils.getFilenameExtension(name.toLowerCase()); // // String nameThumb = name.replaceAll("." + suffix, "-thumb." + suffix); // // String key = "temp/" + name; // String keyThumb = "temp/" + nameThumb; // // // -- [COS] 删除对象 -------------------------------------- // COSClient cosClient = getClient(secretId, secretKey); // cosClient.deleteObject(bucketName, key); // cosClient.deleteObject(bucketName, keyThumb); // cosClient.shutdown(); // // // -- [DB] 判断记录是否存在 -------------------------------- // SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key); // if (fileDetail == null) throw new CustException("文件记录不存在"); // // // -- [DB] 删除存储桶中的文件 ------------------------------- // // -- 创建异步上传任务 --------------------------------------------------------------------- // CompletableFuture.runAsync(() -> { // sysFileMapper.deleteFile(key); // sysFileMapper.deleteFile(keyThumb); // }); // // return Map.of("key", key); // } }