OssFileService.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. package com.fdkankan.fyun.oss;
  2. import cn.hutool.core.collection.CollUtil;
  3. import com.alibaba.fastjson.JSON;
  4. import com.aliyun.oss.HttpMethod;
  5. import com.aliyun.oss.OSS;
  6. import com.aliyun.oss.OSSClientBuilder;
  7. import com.aliyun.oss.common.comm.ResponseMessage;
  8. import com.aliyun.oss.model.*;
  9. import com.fdkankan.common.util.FileMd5Util;
  10. import com.fdkankan.fyun.constant.FYunTypeEnum;
  11. import com.fdkankan.fyun.face.AbstractFYunFileService;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  16. import org.springframework.stereotype.Component;
  17. import org.springframework.util.CollectionUtils;
  18. import org.springframework.util.ObjectUtils;
  19. import org.springframework.util.StringUtils;
  20. import java.io.*;
  21. import java.math.BigDecimal;
  22. import java.net.URL;
  23. import java.util.*;
  24. import java.util.stream.Collectors;
  25. @Component
  26. @ConditionalOnProperty(name = "fyun.type", havingValue = "oss")
  27. public class OssFileService extends AbstractFYunFileService {
  28. private Logger log = LoggerFactory.getLogger(this.getClass().getName());
  29. @Autowired
  30. private OSS ossClient;
  31. @Override
  32. public String uploadFile(String bucket, byte[] data, String remoteFilePath) {
  33. try {
  34. ossClient.putObject(bucket, remoteFilePath, new ByteArrayInputStream(data));
  35. } catch (Exception e) {
  36. log.error("oss上传文件失败,remoteFilePath:" + remoteFilePath, e);
  37. }
  38. return null;
  39. }
  40. @Override
  41. public String uploadFile(String bucket, String filePath, String remoteFilePath) {
  42. return uploadFile(bucket, filePath, remoteFilePath, null);
  43. }
  44. @Override
  45. public String uploadFile(String bucket, InputStream inputStream, String remoteFilePath) {
  46. try {
  47. ossClient.putObject(bucket, remoteFilePath, inputStream);
  48. log.info("文件流上传成功,目标路径:remoteFilePath:{}", remoteFilePath);
  49. } catch (Exception e) {
  50. log.error("oss上传文件失败,remoteFilePath:"+remoteFilePath, e);
  51. }
  52. return null;
  53. }
  54. @Override
  55. public String uploadFile(String bucket, String filePath, String remoteFilePath, Map<String, String> headers) {
  56. try {
  57. File file = new File(filePath);
  58. if (!file.exists()) {
  59. log.warn("要上传的文件不存在,filePath" + filePath);
  60. return null;
  61. }
  62. ObjectMetadata metadata = new ObjectMetadata();
  63. if (filePath.contains(".jpg")) {
  64. metadata.setContentType("image/jpeg");
  65. }
  66. if (filePath.contains(".mp4")) {
  67. metadata.setContentType("video/mp4");
  68. }
  69. if (filePath.contains(".mp3")) {
  70. metadata.setContentType("audio/mp3");
  71. }
  72. if (org.apache.commons.lang3.ObjectUtils.isNotEmpty(headers)) {
  73. for (Map.Entry<String, String> header : headers.entrySet()) {
  74. metadata.setHeader(header.getKey(), header.getValue());
  75. }
  76. }
  77. PutObjectResult putObjectResult = ossClient.putObject(bucket, remoteFilePath, file, metadata);
  78. log.info("文件上传成功,path:{}", filePath);
  79. } catch (Exception e) {
  80. log.error("oss上传文件失败,filePath:"+filePath, e);
  81. }
  82. return null;
  83. }
  84. public static void main(String[] args) {
  85. File file = new File("D:\\Downloads\\SG-t-GjwsdgpLvDb\\wwwroot\\scene_view_data\\SG-t-GjwsdgpLvDb\\images\\pan\\high\\0.jpg");
  86. String fileMD5 = FileMd5Util.getFileMD5(file);
  87. System.out.println("本地文件md5:" + fileMD5);
  88. ObjectMetadata metadata = new ObjectMetadata();
  89. Map<String, String> tags = new HashMap<>();
  90. tags.put("tag-a", file.getName());
  91. metadata.setObjectTagging(tags);
  92. OSS ossClient = new OSSClientBuilder().build("http://oss-cn-shenzhen.aliyuncs.com", "LTAI5tJwboCj3r4vUNkSmbyX", "meDy7VYAWbg8kZCKsoUZcIYQxigWOy");
  93. PutObjectRequest request = new PutObjectRequest("4dkankan", "testdata/0.jpg", file, metadata);
  94. request.setVersionId("123444");
  95. PutObjectResult putObjectResult = ossClient.putObject(request);
  96. String requestId = putObjectResult.getRequestId();
  97. System.out.println("requestId:" + requestId);
  98. System.out.println(System.currentTimeMillis());
  99. boolean exist = ossClient.doesObjectExist("4dkankan", "testdata/0.jpg");
  100. System.out.println(System.currentTimeMillis());
  101. System.out.println("文件是否存在:" + exist);
  102. if(!exist){
  103. System.out.println("文件不存在");
  104. // TODO: 2025/3/31 失败处理
  105. }
  106. putObjectResult.getVersionId();
  107. System.out.println(putObjectResult.getVersionId());
  108. // System.out.println(requestId1.equals(requestId2));
  109. }
  110. @Override
  111. public String uploadFileByCommand(String bucket, String filePath, String remoteFilePath) {
  112. try {
  113. String optType = new File(filePath).isDirectory() ? "folder" : "file";
  114. String command = String.format(fYunConstants.UPLOAD_SH, bucket, filePath, remoteFilePath, FYunTypeEnum.OSS.code(), optType);
  115. log.info("开始上传文件, ossPath:{}, srcPath:{}", remoteFilePath, filePath);
  116. callshell(command);
  117. log.info("上传文件完毕, ossPath:{}, srcPath:{}", remoteFilePath, filePath);
  118. } catch (Exception e) {
  119. log.error(String.format("上传文件失败, ossPath:%s, srcPath:%s", remoteFilePath, filePath), e);
  120. }
  121. return null;
  122. }
  123. @Override
  124. public void downloadFileByCommand(String bucket, String filePath, String remoteFilePath) {
  125. try {
  126. String optType = remoteFilePath.contains(".") ? "file" : "folder";
  127. String command = String.format(fYunConstants.DOWNLOAD_SH, bucket, remoteFilePath, filePath, FYunTypeEnum.OSS.code(), optType);
  128. log.info("开始下载文件, ossPath:{}, srcPath:{}", remoteFilePath, filePath);
  129. callshell(command);
  130. log.info("下载文件完毕, ossPath:{}, srcPath:{}", remoteFilePath, filePath);
  131. } catch (Exception e) {
  132. log.error(String.format("下载文件失败, ossPath:%s, srcPath:%s", remoteFilePath, filePath), e);
  133. }
  134. }
  135. @Override
  136. public void downloadByCommand(String bucket, String filePath, String remoteFilePath, boolean isDir) {
  137. try {
  138. String optType = isDir ? "folder" : "file";
  139. String command = String.format(fYunConstants.DOWNLOAD_SH, bucket, remoteFilePath, filePath, FYunTypeEnum.OSS.code(), optType);
  140. log.info("开始下载文件, ossPath:{}, srcPath:{}", remoteFilePath, filePath);
  141. callshell(command);
  142. log.info("下载文件完毕, ossPath:{}, srcPath:{}", remoteFilePath, filePath);
  143. } catch (Exception e) {
  144. log.error(String.format("下载文件失败, ossPath:%s, srcPath:%s", remoteFilePath, filePath), e);
  145. }
  146. }
  147. @Override
  148. public void deleteFile(String bucket, String remoteFilePath) throws IOException {
  149. try {
  150. ossClient.deleteObject(bucket, remoteFilePath);
  151. } catch (Exception e) {
  152. log.error("OSS删除文件失败,key:" + remoteFilePath, e);
  153. }
  154. }
  155. @Override
  156. public void deleteFolder(String bucket, String remoteFolderPath) {
  157. try {
  158. if (!remoteFolderPath.endsWith("/")) {
  159. remoteFolderPath = remoteFolderPath + "/";
  160. }
  161. log.info("开始删除文件夹:{}", remoteFolderPath);
  162. boolean flag = true;
  163. String nextMaker = null;
  164. ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucket).withPrefix(remoteFolderPath).withMaxKeys(1000);
  165. DeleteObjectsRequest request = new DeleteObjectsRequest(bucket);
  166. do {
  167. //获取下一页的起始点,它的下一项
  168. listObjectsRequest.setMarker(nextMaker);
  169. ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
  170. List<String> keys = objectListing.getObjectSummaries().parallelStream()
  171. .map(OSSObjectSummary::getKey).collect(Collectors.toList());
  172. if (!CollectionUtils.isEmpty(keys)) {
  173. request.setKeys(keys);
  174. ossClient.deleteObjects(request);
  175. }
  176. nextMaker = objectListing.getNextMarker();
  177. //全部执行完后,为false
  178. flag = objectListing.isTruncated();
  179. } while (flag);
  180. } catch (Exception e) {
  181. log.error("OSS删除文件失败,key:" + remoteFolderPath, e);
  182. }
  183. }
  184. @Override
  185. public void uploadMulFiles(String bucket, Map<String, String> filepaths) {
  186. try {
  187. for (Map.Entry<String, String> entry : filepaths.entrySet()) {
  188. uploadFile(bucket, entry.getKey(), entry.getValue(), null);
  189. }
  190. } catch (Exception e) {
  191. log.error("OSS批量上传文件失败!");
  192. }
  193. }
  194. @Override
  195. public List<String> listRemoteFiles(String bucket, String sourcePath) {
  196. List<String> keyList = new ArrayList<>();
  197. try {
  198. boolean flag = true;
  199. String nextMaker = null;
  200. ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucket);
  201. //指定下一级文件
  202. listObjectsRequest.setPrefix(sourcePath);
  203. //设置分页的页容量
  204. listObjectsRequest.setMaxKeys(200);
  205. do {
  206. //获取下一页的起始点,它的下一项
  207. listObjectsRequest.setMarker(nextMaker);
  208. ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
  209. List<String> collect = objectListing.getObjectSummaries().parallelStream()
  210. .map(OSSObjectSummary::getKey).collect(Collectors.toList());
  211. if (!CollectionUtils.isEmpty(collect)) {
  212. keyList.addAll(collect);
  213. }
  214. nextMaker = objectListing.getNextMarker();
  215. //全部执行完后,为false
  216. flag = objectListing.isTruncated();
  217. } while (flag);
  218. } catch (Exception e) {
  219. log.error("获取文件列表失败,path:" + sourcePath, e);
  220. }
  221. return keyList;
  222. }
  223. @Override
  224. public void copyFileBetweenBucketParallel(String sourceBucketName, String sourcePath, String targetBucketName, String targetPath) {
  225. try {
  226. List<String> files = listRemoteFiles(sourceBucketName, sourcePath);
  227. if (ObjectUtils.isEmpty(files)) {
  228. return;
  229. }
  230. files.parallelStream().forEach(file -> {
  231. ossClient.copyObject(sourceBucketName, file, targetBucketName, file.replace(sourcePath, targetPath));
  232. });
  233. } catch (Exception e) {
  234. log.error("列举文件目录失败,key:" + sourcePath, e);
  235. }
  236. }
  237. @Override
  238. public void copyFileBetweenBucket(String sourceBucketName, String sourcePath, String targetBucketName, String targetPath) {
  239. try {
  240. List<String> files = listRemoteFiles(sourceBucketName, sourcePath);
  241. if (ObjectUtils.isEmpty(files)) {
  242. return;
  243. }
  244. files.stream().forEach(file -> {
  245. ossClient.copyObject(sourceBucketName, file, targetBucketName, file.replace(sourcePath, targetPath));
  246. });
  247. } catch (Exception e) {
  248. log.error("列举文件目录失败,key:" + sourcePath, e);
  249. }
  250. }
  251. @Override
  252. public void copyFilesBetweenBucket(String sourceBucketName, String targetBucketName, Map<String, String> pathMap) {
  253. if (ObjectUtils.isEmpty(pathMap)) {
  254. return;
  255. }
  256. try {
  257. for (Map.Entry<String, String> entry : pathMap.entrySet()) {
  258. copyFileBetweenBucket(sourceBucketName, entry.getKey(), targetBucketName, entry.getValue());
  259. }
  260. } catch (Exception e) {
  261. log.error(String.format("批量复制文件失败, sourceBucketName:%s, targetBucketName:%s", sourceBucketName, targetBucketName), e);
  262. }
  263. }
  264. @Override
  265. public String getFileContent(String bucketName, String remoteFilePath) {
  266. try (OSSObject ossObject = ossClient.getObject(bucketName, remoteFilePath)){
  267. InputStream objectContent = ossObject.getObjectContent();
  268. StringBuilder contentJson = new StringBuilder();
  269. try (BufferedReader reader = new BufferedReader(new InputStreamReader(objectContent))) {
  270. while (true) {
  271. String line = reader.readLine();
  272. if (line == null) break;
  273. contentJson.append(line);
  274. }
  275. } catch (IOException e) {
  276. throw e;
  277. }
  278. return contentJson.toString();
  279. } catch (Exception e) {
  280. log.error("获取文件内容失败:key:"+remoteFilePath, e);
  281. }
  282. return null;
  283. }
  284. @Override
  285. public boolean fileExist(String bucket, String objectName) {
  286. try {
  287. return ossClient.doesObjectExist(bucket, objectName);
  288. } catch (Exception e) {
  289. log.error("判断文件是否存在失败,key:"+objectName, e);
  290. }
  291. return false;
  292. }
  293. @Override
  294. public void downloadFile(String bucket, String remoteFilePath, String localPath) {
  295. try {
  296. File localFile = new File(localPath);
  297. if (!localFile.getParentFile().exists()) {
  298. localFile.getParentFile().mkdirs();
  299. }
  300. if(localFile.isDirectory()){
  301. String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/")+1);
  302. log.info("未配置文件名,使用默认文件名:{}",fileName);
  303. localPath = localPath.concat(File.separator).concat(fileName);
  304. }
  305. DownloadFileRequest request = new DownloadFileRequest(bucket, remoteFilePath);
  306. request.setDownloadFile(localPath);
  307. // 默认5个任务并发下载
  308. request.setTaskNum(5);
  309. // 启动断点续传
  310. request.setEnableCheckpoint(true);
  311. ossClient.downloadFile(request);
  312. } catch (Throwable throwable) {
  313. log.error("文件下载失败,key:"+remoteFilePath, throwable);
  314. }
  315. }
  316. @Override
  317. public URL getPresignedUrl(String bucket, String url) {
  318. java.util.Date expiration = new java.util.Date();
  319. long expTimeMillis = expiration.getTime();
  320. expTimeMillis += 1000 * 60 * 60 * 8;
  321. expiration.setTime(expTimeMillis);
  322. GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, url);
  323. generatePresignedUrlRequest.setMethod(HttpMethod.PUT);
  324. generatePresignedUrlRequest.setExpiration(expiration);
  325. return ossClient.generatePresignedUrl(generatePresignedUrlRequest);
  326. }
  327. @Override
  328. public long getSubFileNums(String bucket, String url) {
  329. long totalSubFileNum = 0;
  330. try {
  331. boolean flag = true;
  332. String nextMaker = null;
  333. ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucket);
  334. //指定下一级文件
  335. listObjectsRequest.setPrefix(url);
  336. //设置分页的页容量
  337. listObjectsRequest.setMaxKeys(200);
  338. do {
  339. //获取下一页的起始点,它的下一项
  340. listObjectsRequest.setMarker(nextMaker);
  341. ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
  342. List<String> collect = objectListing.getObjectSummaries().parallelStream()
  343. .map(OSSObjectSummary::getKey).collect(Collectors.toList());
  344. if (!CollectionUtils.isEmpty(collect)) {
  345. totalSubFileNum = totalSubFileNum + collect.size();
  346. }
  347. nextMaker = objectListing.getNextMarker();
  348. //全部执行完后,为false
  349. flag = objectListing.isTruncated();
  350. } while (flag);
  351. } catch (Exception e) {
  352. log.error("获取文件数量失败,path:" + url, e);
  353. }
  354. return totalSubFileNum;
  355. }
  356. @Override
  357. public Boolean checkStore(String bucket,String filePath){
  358. ObjectMetadata objectMetadata = ossClient.getObjectMetadata(bucket, filePath);
  359. return !objectMetadata.isRestoreCompleted();
  360. }
  361. @Override
  362. public void restoreFolder(String bucket,String folderName){
  363. ObjectMetadata objectMetadata ;
  364. List<String> objectList = this.listRemoteFiles(bucket, folderName);
  365. if(CollUtil.isEmpty(objectList)){
  366. return;
  367. }
  368. for (String objectName : objectList) {
  369. objectMetadata = ossClient.getObjectMetadata(bucket, objectName);
  370. // 校验Object是否为归档类型Object。
  371. StorageClass storageClass = objectMetadata.getObjectStorageClass();
  372. if (storageClass == StorageClass.ColdArchive) {
  373. // 解冻Object。
  374. ossClient.restoreObject(bucket, objectName);
  375. }
  376. }
  377. }
  378. @Override
  379. public Integer getRestoreFolderProcess(String bucket,String folderName){
  380. ObjectMetadata objectMetadata ;
  381. List<String> objectList = this.listRemoteFiles(bucket, folderName);
  382. if(CollUtil.isEmpty(objectList)){
  383. return 100;
  384. }
  385. List<String> restoreFileList = new ArrayList<>();
  386. for (String objectName : objectList) {
  387. objectMetadata = ossClient.getObjectMetadata(bucket, objectName);
  388. if(objectMetadata.isRestoreCompleted()){
  389. restoreFileList.add(objectName);
  390. }
  391. }
  392. if(objectList.size() <= restoreFileList.size() ){
  393. return 100;
  394. }
  395. BigDecimal rite = new BigDecimal(restoreFileList.size()).divide(new BigDecimal(objectList.size()),2,BigDecimal.ROUND_HALF_UP);
  396. BigDecimal multiply = rite.multiply(new BigDecimal(100));
  397. return multiply.intValue();
  398. }
  399. @Override
  400. public void restoreFolder(String bucket, String folderName, Integer priority) {
  401. List<String> objectList = this.listRemoteFiles(bucket, folderName);
  402. if(CollUtil.isEmpty(objectList)){
  403. return;
  404. }
  405. objectList.parallelStream().forEach(objectName -> {
  406. this.restoreFile(bucket, objectName, priority);
  407. });
  408. }
  409. @Override
  410. public void restoreFile(String bucket, String objectName, Integer priority){
  411. ObjectMetadata objectMetadata = ossClient.getObjectMetadata(bucket, objectName);
  412. // 校验Object是否为归档类型Object。
  413. StorageClass storageClass = objectMetadata.getObjectStorageClass();
  414. if (storageClass == StorageClass.ColdArchive) {
  415. // 设置解冻冷归档Object的优先级。
  416. // RestoreTier.RESTORE_TIER_EXPEDITED 表示1小时内完成解冻。
  417. // RestoreTier.RESTORE_TIER_STANDARD 表示2~5小时内完成解冻。
  418. // RestoreTier.RESTORE_TIER_BULK 表示5~12小时内完成解冻。
  419. RestoreTier restoreTier = null;
  420. switch (priority){
  421. case 1 :
  422. restoreTier = RestoreTier.RESTORE_TIER_EXPEDITED;
  423. break;
  424. case 2 :
  425. restoreTier = RestoreTier.RESTORE_TIER_STANDARD;
  426. break;
  427. default:
  428. restoreTier = RestoreTier.RESTORE_TIER_BULK;
  429. }
  430. RestoreJobParameters jobParameters = new RestoreJobParameters(restoreTier);
  431. // 配置解冻参数,以设置5小时内解冻完成,解冻状态保持2天为例。
  432. // 第一个参数表示保持解冻状态的天数,默认是1天,此参数适用于解冻Archive(归档)与ColdArchive(冷归档)类型Object。
  433. // 第二个参数jobParameters表示解冻优先级,只适用于解冻ColdArchive类型Object。
  434. RestoreConfiguration configuration = new RestoreConfiguration(1, jobParameters);
  435. //开始解冻
  436. ossClient.restoreObject(bucket, objectName, configuration);
  437. // // 等待解冻完成。
  438. // do {
  439. // try {
  440. // Thread.sleep(1000);
  441. // } catch (InterruptedException e) {
  442. // e.printStackTrace();
  443. // }
  444. // objectMetadata = ossClient.getObjectMetadata(bucket, objectName);
  445. // } while (!objectMetadata.isRestoreCompleted());
  446. }
  447. }
  448. @Override
  449. public Long getSpace(String bucket, String key) {
  450. Long total = 0L;
  451. boolean flag = true;
  452. String nextMaker = null;
  453. ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucket);
  454. //指定下一级文件
  455. listObjectsRequest.setPrefix(key);
  456. //设置分页的页容量
  457. listObjectsRequest.setMaxKeys(200);
  458. do {
  459. //获取下一页的起始点,它的下一项
  460. listObjectsRequest.setMarker(nextMaker);
  461. ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
  462. Long space = objectListing.getObjectSummaries().parallelStream()
  463. .mapToLong(OSSObjectSummary::getSize).sum();
  464. total += space;
  465. nextMaker = objectListing.getNextMarker();
  466. //全部执行完后,为false
  467. flag = objectListing.isTruncated();
  468. } while (flag);
  469. return total;
  470. }
  471. }