• 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(九)文件服务篇(2):集成minio文件服务


    本文承接上篇《从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(九)文件服务篇(1):minio 单机与集群搭建》 ,目的是搭建一个独立的操作文件的服务

    为什么要搭建独立的文件服务,而不是做一个共通某个业务端去引用,主要是有以下几点理由

    1.独立服务可以让前后台有唯一的路由访问,避免嵌入到某个业务端造成的路由混乱
    2.可以独立集群部署,做独立得的文件集群服务
    3.不需要将代码逻辑渗透到前后台,封装性,易用性更好
    4.可以进行权限隔离,做到灵活控制哪些服务可以访问哪些不可以访问
    

    创建文件服务

    由于我们文件服务属于一种支撑服务,而且以后可能会有很多类似的支撑服务,所以我们首先创建一个support支撑服务父项目,添加创建支撑父工程以及文件服务工程目录结构如下:

     

    minio pom.xml依赖

    1. com.squareup.okhttp3
    2. okhttp
    3. compile
    4. io.minio
    5. minio

    添加 files文件服务 nacos 端以及内容明细

     

    mini-cloud-files-biz-dev.yml

    1. security:
    2. oauth2:
    3. resource:
    4. token-info-uri: http://mini-cloud-authentication-center/oauth/check_token
    5. client:
    6. client-id: test-auth-client
    7. client-secret: 123
    8. scope: read
    引入minioclient 配置
    

     

    配置放在本地yml或者nacos中均可,我这里放在nacos便于维护 

    1. minio:
    2. endpoint: http://192.168.1.5:9090
    3. accessKey: pZKVfmfSQMDRHZ3I
    4. secretKey: sswL7XLx5wAwUKY6OJhIp6an7ZIuYPFc
    5. bucketName: test1

     如何设置accessKey 和secretKey

     首先登录到管理页面,点击如下菜单

     

     复制accessKeysecretKey后点击保存

     

    配置 spring boot 上传文件最大大小

    spring:

      servlet:

        multipart:

           max-request-size: 50MB

           max-file-size: 50MB

     

    代码明细

     这里主要贴关键代码

    MinioConfig.java
    1. @Configuration
    2. public class MinioConfig {
    3. @Value("${minio.endpoint}")
    4. private String endpoint;
    5. @Value("${minio.accessKey}")
    6. private String accessKey;
    7. @Value("${minio.secretKey}")
    8. private String secretKey;
    9. @Bean
    10. public MinioClient minioClient() {
    11. return MinioClient.builder()
    12. .endpoint(endpoint)
    13. .credentials(accessKey, secretKey)
    14. .build();
    15. }
    16. }
    MinioUtil.java
    
    1. @Component
    2. public class MinioUtil {
    3. @Autowired
    4. private MinioClient client;
    5. @Value("${minio.bucketName}")
    6. private String bucketName;
    7. private static final String SEPARATOR_DOT = ".";
    8. private static final String SEPARATOR_ACROSS = "-";
    9. private static final String SEPARATOR_STR = "";
    10. // 存储桶名称
    11. private static final String chunkBucKet = "img";
    12. /**
    13. * 不排序
    14. */
    15. public final static boolean NOT_SORT = false;
    16. /**
    17. * 排序
    18. */
    19. public final static boolean SORT = true;
    20. /**
    21. * 默认过期时间(分钟)
    22. */
    23. private final static Integer DEFAULT_EXPIRY = 60;
    24. /**
    25. * 删除分片
    26. */
    27. public final static boolean DELETE_CHUNK_OBJECT = true;
    28. /**
    29. * 不删除分片
    30. */
    31. public final static boolean NOT_DELETE_CHUNK_OBJECT = false;
    32. /**
    33. * @param bucketName
    34. * @return boolean
    35. * @Description 判断 bucket是否存在
    36. * @author exe.wangtaotao
    37. * @date 2020/10/21 16:33
    38. */
    39. public boolean bucketExists(String bucketName) {
    40. try {
    41. return client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    42. } catch (Exception e) {
    43. e.printStackTrace();
    44. }
    45. return false;
    46. }
    47. /**
    48. * 创建存储桶
    49. * 创建 bucket
    50. *
    51. * @param bucketName
    52. */
    53. public void makeBucket(String bucketName) {
    54. try {
    55. boolean isExist = bucketExists(bucketName);
    56. if (!isExist) {
    57. client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    58. }
    59. } catch (Exception e) {
    60. e.printStackTrace();
    61. }
    62. }
    63. /**
    64. * @param
    65. * @return java.util.List
    66. * @Description 获取文件存储服务的所有存储桶名称
    67. * @author exe.wangtaotao
    68. * @date 2020/10/21 16:35
    69. */
    70. public List listBucketNames() {
    71. List bucketList = listBuckets();
    72. List bucketListName = new ArrayList<>();
    73. for (Bucket bucket : bucketList) {
    74. bucketListName.add(bucket.name());
    75. }
    76. return bucketListName;
    77. }
    78. /**
    79. * @return java.util.List
    80. * @Description 列出所有存储桶
    81. */
    82. @SneakyThrows
    83. private List listBuckets() {
    84. return client.listBuckets();
    85. }
    86. /**
    87. * 获取对象文件名称列表
    88. *
    89. * @param bucketName 存储桶名称
    90. * @param prefix 对象名称前缀(文件夹 /xx/xx/xxx.jpg 中的 /xx/xx/)
    91. * @return objectNames
    92. */
    93. public List listObjectNames(String bucketName, String prefix) {
    94. return listObjectNames(bucketName, prefix, NOT_SORT);
    95. }
    96. /**
    97. * 获取对象文件名称列表
    98. *
    99. * @param bucketName 存储桶名称
    100. * @param prefix 对象名称前缀(文件夹 /xx/xx/xxx.jpg 中的 /xx/xx/)
    101. * @param sort 是否排序(升序)
    102. * @return objectNames
    103. */
    104. @SneakyThrows
    105. public List listObjectNames(String bucketName, String prefix, Boolean sort) {
    106. boolean flag = bucketExists(bucketName);
    107. if (flag) {
    108. ListObjectsArgs listObjectsArgs;
    109. if (null == prefix) {
    110. listObjectsArgs = ListObjectsArgs.builder()
    111. .bucket(bucketName)
    112. .recursive(true)
    113. .build();
    114. } else {
    115. listObjectsArgs = ListObjectsArgs.builder()
    116. .bucket(bucketName)
    117. .prefix(prefix)
    118. .recursive(true)
    119. .build();
    120. }
    121. Iterable> chunks = client.listObjects(listObjectsArgs);
    122. List chunkPaths = new ArrayList<>();
    123. for (Result item : chunks) {
    124. chunkPaths.add(item.get().objectName());
    125. }
    126. if (sort) {
    127. chunkPaths.sort(new Str2IntComparator(false));
    128. }
    129. return chunkPaths;
    130. }
    131. return new ArrayList<>();
    132. }
    133. /**
    134. * 在桶下创建文件夹,文件夹层级结构根据参数决定
    135. *
    136. * @param bucket 桶名称
    137. * @param WotDir 格式为 xxx/xxx/xxx/
    138. */
    139. @SneakyThrows
    140. public String createDirectory(String bucket, String WotDir) {
    141. if (!this.bucketExists(bucket)) {
    142. return null;
    143. }
    144. client.putObject(PutObjectArgs.builder().bucket(bucket).object(WotDir).stream(
    145. new ByteArrayInputStream(new byte[]{}), 0, -1)
    146. .build());
    147. return WotDir;
    148. }
    149. /**
    150. * 删除一个文件
    151. *
    152. * @param bucketName
    153. * @param objectName
    154. */
    155. @SneakyThrows
    156. public boolean removeObject(String bucketName, String objectName) {
    157. if (!bucketExists(bucketName)) {
    158. return false;
    159. }
    160. client.removeObject(
    161. RemoveObjectArgs.builder()
    162. .bucket(bucketName)
    163. .object(objectName)
    164. .build());
    165. return true;
    166. }
    167. /**
    168. * @param bucketName
    169. * @param objectNames
    170. * @return java.util.List
    171. * @Description 删除指定桶的多个文件对象, 返回删除错误的对象列表,全部删除成功,返回空列表
    172. * @author exe.wangtaotao
    173. * @date 2020/10/21 16:43
    174. */
    175. @SneakyThrows
    176. public List removeObjects(String bucketName, List objectNames) {
    177. if (!bucketExists(bucketName)) {
    178. return new ArrayList<>();
    179. }
    180. List deleteObjects = new ArrayList<>(objectNames.size());
    181. for (String objectName : objectNames) {
    182. deleteObjects.add(new DeleteObject(objectName));
    183. }
    184. List deleteErrorNames = new ArrayList<>();
    185. Iterable> results = client.removeObjects(
    186. RemoveObjectsArgs.builder()
    187. .bucket(bucketName)
    188. .objects(deleteObjects)
    189. .build());
    190. for (Result result : results) {
    191. DeleteError error = result.get();
    192. deleteErrorNames.add(error.objectName());
    193. }
    194. return deleteErrorNames;
    195. }
    196. /**
    197. * 获取访问对象的外链地址
    198. * 获取文件的下载url
    199. *
    200. * @param bucketName 存储桶名称
    201. * @param objectName 对象名称
    202. * @param expiry 过期时间(分钟) 最大为7天 超过7天则默认最大值
    203. * @return viewUrl
    204. */
    205. @SneakyThrows
    206. public String getObjectUrl(String bucketName, String objectName, Integer expiry) {
    207. expiry = expiryHandle(expiry);
    208. return client.getPresignedObjectUrl(
    209. GetPresignedObjectUrlArgs.builder()
    210. .method(Method.GET)
    211. .bucket(bucketName)
    212. .object(objectName)
    213. .expiry(expiry)
    214. .build()
    215. );
    216. }
    217. /**
    218. * 创建上传文件对象的外链
    219. *
    220. * @param bucketName 存储桶名称
    221. * @param objectName 欲上传文件对象的名称
    222. * @return uploadUrl
    223. */
    224. public String createUploadUrl(String bucketName, String objectName) {
    225. return createUploadUrl(bucketName, objectName, DEFAULT_EXPIRY);
    226. }
    227. /**
    228. * 创建上传文件对象的外链
    229. *
    230. * @param bucketName 存储桶名称
    231. * @param objectName 欲上传文件对象的名称
    232. * @param expiry 过期时间(分钟) 最大为7天 超过7天则默认最大值
    233. * @return uploadUrl
    234. */
    235. @SneakyThrows
    236. public String createUploadUrl(String bucketName, String objectName, Integer expiry) {
    237. expiry = expiryHandle(expiry);
    238. return client.getPresignedObjectUrl(
    239. GetPresignedObjectUrlArgs.builder()
    240. .method(Method.PUT)
    241. .bucket(bucketName)
    242. .object(objectName)
    243. .expiry(expiry)
    244. .build()
    245. );
    246. }
    247. /**
    248. * 文件下载
    249. *
    250. * @param fileName 文件名
    251. * @return InputStream
    252. */
    253. public void downLoadFile(HttpServletResponse response,String fileName) {
    254. /* InputStream inputStream = null;
    255. try {
    256. StatObjectResponse statObjectResponse = client.statObject(StatObjectArgs.builder().bucket(chunkBucKet).object(fileName).build());
    257. if (statObjectResponse.size() > 0) {
    258. inputStream = client.getObject(GetObjectArgs.builder().bucket(chunkBucKet).object(fileName).build());
    259. }
    260. } catch (Exception e) {
    261. e.printStackTrace();
    262. }
    263. return inputStream;*/
    264. try(InputStream ism = new BufferedInputStream(client.getObject(GetObjectArgs.builder()
    265. .bucket(chunkBucKet)
    266. .object(fileName).build()))) {
    267. // 调用statObject()来判断对象是否存在。
    268. // 如果不存在, statObject()抛出异常,
    269. // 否则则代表对象存在
    270. client.statObject(StatObjectArgs.builder()
    271. .bucket(chunkBucKet)
    272. .object(fileName).build());
    273. byte buf[] = new byte[1024];
    274. int length = 0;
    275. response.reset();
    276. //Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
    277. // Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,
    278. // 文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
    279. response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
    280. response.setContentType("application/x-msdownload");
    281. response.setCharacterEncoding("utf-8");
    282. OutputStream osm = new BufferedOutputStream(response.getOutputStream());
    283. while ((length = ism.read(buf))>0) {
    284. osm.write(buf,0, length);
    285. }
    286. osm.close();
    287. } catch (Exception ex) {
    288. ex.printStackTrace();
    289. }
    290. }
    291. /**
    292. * 批量下载
    293. *
    294. * @param directory
    295. * @return
    296. */
    297. @SneakyThrows
    298. public List downLoadMore(String bucket, String directory) {
    299. Iterable> objs = client.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(directory).useUrlEncodingType(false).build());
    300. List list = new ArrayList<>();
    301. for (Result result : objs) {
    302. String objectName = null;
    303. objectName = result.get().objectName();
    304. StatObjectResponse statObject = client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectName).build());
    305. if (statObject != null && statObject.size() > 0) {
    306. String fileurl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).object(statObject.object()).method(Method.GET).build());
    307. list.add(fileurl);
    308. }
    309. }
    310. return list;
    311. }
    312. /**
    313. * @param multipartFile
    314. * @param bucketName
    315. * @param directory image/
    316. * @return java.lang.String
    317. * @Description 文件上传
    318. * @author exe.wangtaotao
    319. * @date 2020/10/21 13:45
    320. */
    321. public MinioResponseDTO upload(MultipartFile multipartFile, String bucketName, String directory) throws Exception {
    322. InputStream inputStream = multipartFile.getInputStream();
    323. directory = Optional.ofNullable(directory).orElse("");
    324. String originFilename = Base64.getEncoder().encodeToString(multipartFile.getOriginalFilename().getBytes());
    325. String username = SecurityContextHolder.getContext().getAuthentication().getName();
    326. String minFileName = directory + minFileName(multipartFile.getOriginalFilename());
    327. Map userMetadata = new HashMap<>();
    328. userMetadata.put("originFilename", originFilename);
    329. userMetadata.put("username", username);
    330. userMetadata.put("size", inputStream.available()+"");
    331. //上传文件到指定目录
    332. client.putObject(PutObjectArgs.builder()
    333. .bucket(bucketName)
    334. .object(minFileName)
    335. .contentType(multipartFile.getContentType())
    336. .stream(inputStream, inputStream.available(), -1)
    337. .userMetadata(userMetadata)
    338. .extraQueryParams(userMetadata)
    339. .build());
    340. inputStream.close();
    341. String url = getObjectUrl(bucketName, minFileName, DEFAULT_EXPIRY);
    342. MinioResponseDTO minioResponseDTO = new MinioResponseDTO(minFileName,originFilename,username,url);
    343. return minioResponseDTO;
    344. }
    345. /**
    346. * @param response
    347. * @return java.lang.String
    348. * @Description 下载文件
    349. * @author exe.wangtaotao
    350. * @date 2020/10/21 15:18
    351. */
    352. public void download(HttpServletResponse response, String bucketName, String minFileName) {
    353. InputStream fileInputStream = null;
    354. try {
    355. fileInputStream = client.getObject(GetObjectArgs.builder()
    356. .bucket(bucketName)
    357. .object(minFileName).build());
    358. response.setHeader("Content-Disposition", "attachment;filename=" + minFileName);
    359. response.setContentType("application/force-download");
    360. response.setCharacterEncoding("UTF-8");
    361. IOUtils.copy(fileInputStream, response.getOutputStream());
    362. } catch (ErrorResponseException e) {
    363. e.printStackTrace();
    364. } catch (InsufficientDataException e) {
    365. e.printStackTrace();
    366. } catch (InternalException e) {
    367. e.printStackTrace();
    368. } catch (InvalidKeyException e) {
    369. e.printStackTrace();
    370. } catch (InvalidResponseException e) {
    371. e.printStackTrace();
    372. } catch (IOException e) {
    373. e.printStackTrace();
    374. } catch (NoSuchAlgorithmException e) {
    375. e.printStackTrace();
    376. } catch (ServerException e) {
    377. e.printStackTrace();
    378. } catch (XmlParserException e) {
    379. e.printStackTrace();
    380. } finally {
    381. //关闭流
    382. try {
    383. fileInputStream.close();
    384. } catch (IOException e) {
    385. e.printStackTrace();
    386. }
    387. }
    388. }
    389. /**
    390. * 批量创建分片上传外链
    391. *
    392. * @param bucketName 存储桶名称
    393. * @param objectMD5 欲上传分片文件主文件的MD5
    394. * @param chunkCount 分片数量
    395. * @return uploadChunkUrls
    396. */
    397. public List createUploadChunkUrlList(String bucketName, String objectMD5, Integer chunkCount) {
    398. if (null == bucketName) {
    399. bucketName = chunkBucKet;
    400. }
    401. if (null == objectMD5) {
    402. return null;
    403. }
    404. objectMD5 += "/";
    405. if (null == chunkCount || 0 == chunkCount) {
    406. return null;
    407. }
    408. List urlList = new ArrayList<>(chunkCount);
    409. for (int i = 1; i <= chunkCount; i++) {
    410. String objectName = objectMD5 + i + ".chunk";
    411. urlList.add(createUploadUrl(bucketName, objectName, DEFAULT_EXPIRY));
    412. }
    413. return urlList;
    414. }
    415. /**
    416. * 创建指定序号的分片文件上传外链
    417. *
    418. * @param bucketName 存储桶名称
    419. * @param objectMD5 欲上传分片文件主文件的MD5
    420. * @param partNumber 分片序号
    421. * @return uploadChunkUrl
    422. */
    423. public String createUploadChunkUrl(String bucketName, String objectMD5, Integer partNumber) {
    424. if (null == bucketName) {
    425. bucketName = chunkBucKet;
    426. }
    427. if (null == objectMD5) {
    428. return null;
    429. }
    430. objectMD5 += "/" + partNumber + ".chunk";
    431. return createUploadUrl(bucketName, objectMD5, DEFAULT_EXPIRY);
    432. }
    433. /**
    434. * 获取分片文件名称列表
    435. *
    436. * @param bucketName 存储桶名称
    437. * @param ObjectMd5 对象Md5
    438. * @return objectChunkNames
    439. */
    440. public List listChunkObjectNames(String bucketName, String ObjectMd5) {
    441. if (null == bucketName) {
    442. bucketName = chunkBucKet;
    443. }
    444. if (null == ObjectMd5) {
    445. return null;
    446. }
    447. return listObjectNames(bucketName, ObjectMd5, SORT);
    448. }
    449. /**
    450. * 获取分片名称地址HashMap key=分片序号 value=分片文件地址
    451. *
    452. * @param bucketName 存储桶名称
    453. * @param ObjectMd5 对象Md5
    454. * @return objectChunkNameMap
    455. */
    456. public Map mapChunkObjectNames(String bucketName, String ObjectMd5) {
    457. if (null == bucketName) {
    458. bucketName = chunkBucKet;
    459. }
    460. if (null == ObjectMd5) {
    461. return null;
    462. }
    463. List chunkPaths = listObjectNames(bucketName, ObjectMd5);
    464. if (null == chunkPaths || chunkPaths.size() == 0) {
    465. return null;
    466. }
    467. Map chunkMap = new HashMap<>(chunkPaths.size());
    468. for (String chunkName : chunkPaths) {
    469. Integer partNumber = Integer.parseInt(chunkName.substring(chunkName.indexOf("/") + 1, chunkName.lastIndexOf(".")));
    470. chunkMap.put(partNumber, chunkName);
    471. }
    472. return chunkMap;
    473. }
    474. /**
    475. * 合并分片文件成对象文件
    476. *
    477. * @param chunkBucKetName 分片文件所在存储桶名称
    478. * @param composeBucketName 合并后的对象文件存储的存储桶名称
    479. * @param chunkNames 分片文件名称集合
    480. * @param objectName 合并后的对象文件名称
    481. * @return true/false
    482. */
    483. @SneakyThrows
    484. public boolean composeObject(String chunkBucKetName, String composeBucketName, List chunkNames, String objectName, boolean isDeleteChunkObject) {
    485. if (null == chunkBucKetName) {
    486. chunkBucKetName = chunkBucKet;
    487. }
    488. List sourceObjectList = new ArrayList<>(chunkNames.size());
    489. for (String chunk : chunkNames) {
    490. sourceObjectList.add(
    491. ComposeSource.builder()
    492. .bucket(chunkBucKetName)
    493. .object(chunk)
    494. .build()
    495. );
    496. }
    497. client.composeObject(
    498. ComposeObjectArgs.builder()
    499. .bucket(composeBucketName)
    500. .object(objectName)
    501. .sources(sourceObjectList)
    502. .build()
    503. );
    504. if (isDeleteChunkObject) {
    505. removeObjects(chunkBucKetName, chunkNames);
    506. }
    507. return true;
    508. }
    509. /**
    510. * 合并分片文件成对象文件
    511. *
    512. * @param bucketName 存储桶名称
    513. * @param chunkNames 分片文件名称集合
    514. * @param objectName 合并后的对象文件名称
    515. * @return true/false
    516. */
    517. public boolean composeObject(String bucketName, List chunkNames, String objectName) {
    518. return composeObject(chunkBucKet, bucketName, chunkNames, objectName, NOT_DELETE_CHUNK_OBJECT);
    519. }
    520. /**
    521. * 合并分片文件成对象文件
    522. *
    523. * @param bucketName 存储桶名称
    524. * @param chunkNames 分片文件名称集合
    525. * @param objectName 合并后的对象文件名称
    526. * @return true/false
    527. */
    528. public boolean composeObject(String bucketName, List chunkNames, String objectName, boolean isDeleteChunkObject) {
    529. return composeObject(chunkBucKet, bucketName, chunkNames, objectName, isDeleteChunkObject);
    530. }
    531. /**
    532. * 合并分片文件,合并成功后删除分片文件
    533. *
    534. * @param bucketName 存储桶名称
    535. * @param chunkNames 分片文件名称集合
    536. * @param objectName 合并后的对象文件名称
    537. * @return true/false
    538. */
    539. public boolean composeObjectAndRemoveChunk(String bucketName, List chunkNames, String objectName) {
    540. return composeObject(chunkBucKet, bucketName, chunkNames, objectName, DELETE_CHUNK_OBJECT);
    541. }
    542. /**
    543. * @param originalFileName
    544. * @return java.lang.String
    545. * @Description 生成上传文件名
    546. * @author exe.wangtaotao
    547. * @date 2020/10/21 15:07
    548. */
    549. private String minFileName(String originalFileName) {
    550. String suffix = FilenameUtils.getExtension(originalFileName);
    551. return UUID.randomUUID().toString().replace(SEPARATOR_ACROSS, SEPARATOR_STR).toUpperCase() +"."+ suffix;
    552. }
    553. /**
    554. * 将分钟数转换为秒数
    555. *
    556. * @param expiry 过期时间(分钟数)
    557. * @return expiry
    558. */
    559. private static int expiryHandle(Integer expiry) {
    560. expiry = expiry * 60;
    561. if (expiry > 604800) {
    562. return 604800;
    563. }
    564. return expiry;
    565. }
    566. public Map metadata(String bucketName ,String filename) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
    567. return client.getObject(GetObjectArgs.builder().bucket(bucketName).object(filename).build()).headers().toMultimap();
    568. }
    569. static class Str2IntComparator implements Comparator {
    570. private final boolean reverseOrder; // 是否倒序
    571. public Str2IntComparator(boolean reverseOrder) {
    572. this.reverseOrder = reverseOrder;
    573. }
    574. @Override
    575. public int compare(String arg0, String arg1) {
    576. Integer intArg0 = Integer.parseInt(arg0.substring(arg0.indexOf("/") + 1, arg0.lastIndexOf(".")));
    577. Integer intArg1 = Integer.parseInt(arg1.substring(arg1.indexOf("/") + 1, arg1.lastIndexOf(".")));
    578. if (reverseOrder) {
    579. return intArg1 - intArg0;
    580. } else {
    581. return intArg0 - intArg1;
    582. }
    583. }
    584. }
    FilesController.java
    
    1. @RestController
    2. @RequestMapping("/files")
    3. public class FilesController {
    4. @Autowired
    5. private FilesService filesService;
    6. /**
    7. * 上传文件
    8. * */
    9. @PostMapping
    10. public ResponseEntity save(@RequestParam MultipartFile file) throws Exception {
    11. return ResponseEntity.ok(filesService.save(file));
    12. }
    13. /**
    14. * 下载文件
    15. * */
    16. @GetMapping
    17. public ResponseEntity save(HttpServletResponse response, @RequestParam String filename) throws Exception {
    18. filesService.download(response,filename);
    19. return ResponseEntity.ok().build();
    20. }
    21. /**
    22. * 下载文件
    23. * */
    24. @GetMapping("/metadata")
    25. public ResponseEntity metadata(@RequestParam String filename) throws Exception {
    26. return ResponseEntity.ok(filesService.metadata(filename));
    27. }
    28. }

    FilesServiceImpl.java
    
    1. @Service
    2. public class FilesServiceImpl implements FilesService {
    3. @Autowired
    4. private MinioUtil minioUtil;
    5. @Value("${minio.bucketName}")
    6. private String bucketName;
    7. @Override
    8. @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    9. public Integer save(MultipartFile file) throws Exception {
    10. LocalDate localDate = LocalDate.now();
    11. String dir = DateTimeFormatter.ofPattern("YYYYMMdd").format(localDate);
    12. minioUtil.upload(file,bucketName,dir);
    13. return 1;
    14. }
    15. @Override
    16. public void download(HttpServletResponse response , String filename) {
    17. minioUtil.download(response,bucketName,filename);
    18. }
    19. @SneakyThrows
    20. @Override
    21. public MinioResponseDTO metadata(String filename) {
    22. Map metadataMap = minioUtil.metadata(bucketName,filename);
    23. String originFileName = (String) ((ArrayList) metadataMap.get("x-amz-meta-originfilename")).get(0);
    24. String username = (String) ((ArrayList) metadataMap.get("x-amz-meta-username")).get(0);
    25. originFileName = new String(Base64.getDecoder().decode(originFileName));
    26. MinioResponseDTO minioResponseDTO = new MinioResponseDTO(filename,originFileName,username);
    27. return minioResponseDTO;
    28. }
    29. }

    MinioResponseDTO.java
    1. public class MinioResponseDTO implements Serializable {
    2. private String filename;
    3. private String originFilename;
    4. private String username ;
    5. private String url ;
    6. public MinioResponseDTO(String fileName,String originFilename,String username) {
    7. this.filename = fileName;
    8. this.originFilename = originFilename;
    9. this.username = username;
    10. }
    11. public MinioResponseDTO(String fileName,String originFilename,String username, String url) {
    12. this.filename = fileName;
    13. this.originFilename = originFilename;
    14. this.username = username;
    15. this.url = url;
    16. }
    17. public String getUrl() {
    18. return url;
    19. }
    20. public String getFilename() {
    21. return filename;
    22. }
    23. public String getOriginFilename() {
    24. return originFilename;
    25. }
    26. public String getUsername() {
    27. return username;
    28. }
    29. }

  • 相关阅读:
    PHP 图片的合并,微信小程序码合并,文字合并
    避免 PostgreSQL 翻车的关键技巧
    FFmpeg+javacpp+javacv使用
    【学习笔记】 CF850F Rainbow Balls
    K8S之初入师门第一篇熟读手册
    Python中使用 for 循环来拿遍历 List 的值
    Elasticsearch学习(一)
    HIVE数据导入ES并避免字段空值占用空间
    Flutter 常见错误记录总结
    【Qt】Qt5.15、Qt6在线安装(使用国内源)
  • 原文地址:https://blog.csdn.net/madness1010/article/details/126128265