修复漏洞问题
This commit is contained in:
parent
12bd9c20c3
commit
2bcedea253
|
|
@ -57,7 +57,7 @@ public interface RemoteConfigService {
|
|||
* @return 结果
|
||||
*/
|
||||
@PutMapping
|
||||
public AjaxResult edit(@Validated @RequestBody SysConfig config,@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
public AjaxResult edit(@Validated @RequestBody SysConfig config, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
|
||||
/**
|
||||
* 删除参数配置
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import com.bonus.common.core.constant.SecurityConstants;
|
|||
import com.bonus.common.core.constant.ServiceNameConstants;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.domain.SysDictData;
|
||||
import com.bonus.system.api.domain.SysDictType;
|
||||
import com.bonus.system.api.factory.RemoteDictTypeFallbackFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author wangvivi
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.bonus.common.core.constant.ServiceNameConstants;
|
|||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.domain.SysPost;
|
||||
import com.bonus.system.api.factory.RemoteDeptFallbackFactory;
|
||||
import com.bonus.system.api.factory.RemotePostFallbackFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.bonus.common.core.web.page.TableDataInfo;
|
|||
import com.bonus.system.api.domain.SysRole;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.domain.SysUserRole;
|
||||
import com.bonus.system.api.factory.RemoteDeptFallbackFactory;
|
||||
import com.bonus.system.api.factory.RemoteRoleFallbackFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.bonus.system.api;
|
||||
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.domain.SysDept;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ public class SysConfig extends BaseEntity
|
|||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
//用于excel导出的序号一列,不需要业务逻辑处理
|
||||
@Excel(name = "序号", isSequence = true, type = Excel.Type.EXPORT)
|
||||
int sequence;
|
||||
|
||||
/** 参数主键 */
|
||||
@Excel(name = "参数主键", cellType = ColumnType.NUMERIC)
|
||||
private Long configId;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.bonus.system.api.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
|
|||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Builder
|
||||
public class SysFile
|
||||
{
|
||||
/**
|
||||
|
|
@ -16,9 +18,13 @@ public class SysFile
|
|||
private String name;
|
||||
|
||||
/**
|
||||
* 文件地址
|
||||
* 文件地址,除mongodb 存fileid之外,其他均存上传文件的网络路径
|
||||
*/
|
||||
private String url;
|
||||
// /**
|
||||
// * 文件存储类型,包括 本地、obs、mongodb等
|
||||
// */
|
||||
// private String storageType;
|
||||
|
||||
public String getName()
|
||||
{
|
||||
|
|
@ -47,4 +53,12 @@ public class SysFile
|
|||
.append("url", getUrl())
|
||||
.toString();
|
||||
}
|
||||
|
||||
// public String getStorageType() {
|
||||
// return storageType;
|
||||
// }
|
||||
//
|
||||
// public void setStorageType(String storageType) {
|
||||
// this.storageType = storageType;
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ public class SysPost extends BaseEntity
|
|||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
//用于excel导出的序号一列,不需要业务逻辑处理
|
||||
@Excel(name = "序号", isSequence = true, type = Excel.Type.EXPORT)
|
||||
int sequence;
|
||||
|
||||
/** 岗位序号 */
|
||||
@Excel(name = "岗位序号", cellType = ColumnType.NUMERIC)
|
||||
private Long postId;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.bonus.system.api.factory;
|
||||
|
||||
import com.bonus.common.core.constant.HttpStatus;
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.RemoteConfigService;
|
||||
|
|
@ -43,11 +41,11 @@ public class RemoteConfigFallbackFactory implements FallbackFactory<RemoteConfig
|
|||
}
|
||||
|
||||
@Override
|
||||
public AjaxResult getInfo(Long configId, String source){
|
||||
public AjaxResult getInfo(Long configId, String source){
|
||||
return AjaxResult.error("根据参数编号获取参数配置列表失败:" + throwable.getMessage());
|
||||
}
|
||||
@Override
|
||||
public AjaxResult getConfigKey(String configKey, String source){
|
||||
public AjaxResult getConfigKey(String configKey, String source){
|
||||
return AjaxResult.error("根据参数键名获取参数配置列表失败:" + throwable.getMessage());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.bonus.system.api.factory;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.system.api.RemoteDeptService;
|
||||
import com.bonus.system.api.domain.SysDept;
|
||||
|
|
@ -8,8 +7,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 用户服务降级处理
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ package com.bonus.system.api.factory;
|
|||
import com.bonus.common.core.constant.HttpStatus;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.RemoteDeptService;
|
||||
import com.bonus.system.api.RemoteDictDataService;
|
||||
import com.bonus.system.api.domain.SysConfig;
|
||||
import com.bonus.system.api.domain.SysDept;
|
||||
import com.bonus.system.api.domain.SysDictData;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.bonus.common.core.constant.HttpStatus;
|
|||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.RemoteDictTypeService;
|
||||
import com.bonus.system.api.domain.SysDictData;
|
||||
import com.bonus.system.api.domain.SysDictType;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.bonus.common.core.constant.HttpStatus;
|
|||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.RemoteNoticeService;
|
||||
import com.bonus.system.api.domain.SysDictData;
|
||||
import com.bonus.system.api.domain.SysNotice;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.bonus.common.core.constant.HttpStatus;
|
|||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.RemotePostService;
|
||||
import com.bonus.system.api.domain.SysNotice;
|
||||
import com.bonus.system.api.domain.SysPost;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.bonus.common.core.constant.HttpStatus;
|
|||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.RemoteRoleService;
|
||||
import com.bonus.system.api.domain.SysPost;
|
||||
import com.bonus.system.api.domain.SysRole;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.domain.SysUserRole;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.bonus.common.core.constant.HttpStatus;
|
|||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.core.web.page.TableDataInfo;
|
||||
import com.bonus.system.api.domain.SysDept;
|
||||
import com.bonus.system.api.domain.SysRole;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -15,7 +14,6 @@ import com.bonus.system.api.RemoteUserService;
|
|||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -71,6 +71,59 @@
|
|||
<artifactId>commons-net</artifactId>
|
||||
<version>3.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.huaweicloud</groupId>
|
||||
<artifactId>esdk-obs-java-bundle</artifactId>
|
||||
<version>3.23.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.15.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.15.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.15.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.15.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.mongodb</groupId>-->
|
||||
<!-- <artifactId>mongodb-driver-sync</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.12</version> <!-- 版本号可以根据需要调整 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.bonus.oss.config;
|
||||
package com.bonus.file.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.bonus.obs.config;
|
||||
package com.bonus.file.config;
|
||||
|
||||
import com.obs.services.ObsClient;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
|
|
@ -14,8 +12,6 @@ public class ObsConfig {
|
|||
private String sk;
|
||||
private String bucket;
|
||||
|
||||
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
|
@ -1,28 +1,26 @@
|
|||
package com.bonus.file.controller;
|
||||
|
||||
import com.bonus.common.core.constant.HttpStatus;
|
||||
import com.bonus.common.core.utils.Base64Utils;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.file.service.ISysFileService;
|
||||
import com.bonus.system.api.domain.SysFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 文件请求处理
|
||||
*
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@RestController
|
||||
|
|
@ -31,11 +29,17 @@ public class SysFileController
|
|||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(SysFileController.class);
|
||||
|
||||
private final ISysFileService sysFileService;
|
||||
|
||||
@Autowired
|
||||
private ISysFileService sysFileService;
|
||||
public SysFileController(ISysFileService fileService) {
|
||||
this.sysFileService = fileService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传请求
|
||||
* 单文件上传
|
||||
* @param file 单个文件
|
||||
* @return 文件信息,包括文件名和文件路径
|
||||
*/
|
||||
@PostMapping("upload")
|
||||
@ApiOperation("上传本地文件到服务器")
|
||||
|
|
@ -44,10 +48,7 @@ public class SysFileController
|
|||
try
|
||||
{
|
||||
// 上传并返回访问地址
|
||||
String url = sysFileService.uploadFile(file);
|
||||
SysFile sysFile = new SysFile();
|
||||
sysFile.setName(FileUtils.getName(url));
|
||||
sysFile.setUrl(url);
|
||||
SysFile sysFile = sysFileService.uploadFile(file);
|
||||
return R.ok(sysFile);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -57,40 +58,57 @@ public class SysFileController
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/download")
|
||||
@ApiOperation("从服务器下载文件")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "url", value = "文件网络地址", required = true),
|
||||
@ApiImplicitParam(name = "destination", value = "下载的本地路径", required = true),
|
||||
})
|
||||
public R<Boolean> downloadFile(String url, String destination) {
|
||||
/**
|
||||
* 多文件上传
|
||||
* @param files 多个文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@PostMapping("/uploadFiles")
|
||||
public AjaxResult uploadFile(MultipartFile[] files) {
|
||||
try {
|
||||
String fileUrl = Base64Utils.decodeUrl(url);
|
||||
if (fileUrl != null) {
|
||||
String fileName = Base64Utils.getFileNameFromUrl(fileUrl);
|
||||
sysFileService.downloadFile(fileUrl, destination + "/" + fileName);
|
||||
return R.ok();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("下载文件失败", e);
|
||||
return R.fail(e.getMessage());
|
||||
return AjaxResult.success(sysFileService.uploadFiles(files));
|
||||
} catch (Exception e)
|
||||
{
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
return R.fail("下载文件失败");
|
||||
}
|
||||
|
||||
@GetMapping("/delete")
|
||||
public R<Boolean> deleteFile(String url) {
|
||||
/**
|
||||
* 文件下载
|
||||
* @param response 请求响应
|
||||
* @param objectKey,除mongodb 存fileid之外,其他均存上传文件的网络路径
|
||||
*/
|
||||
@GetMapping("/download")
|
||||
public void downloadFile(HttpServletResponse response, @RequestParam String objectKey) throws IOException {
|
||||
try {
|
||||
String fileUrl = Base64Utils.decodeUrl(url);
|
||||
if (fileUrl != null) {
|
||||
sysFileService.deleteFile(fileUrl);
|
||||
return R.ok();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("delete文件失败", e);
|
||||
return R.fail(e.getMessage());
|
||||
String fileUrl = Base64Utils.decodeUrl(objectKey);
|
||||
sysFileService.downloadFile(response, fileUrl);
|
||||
} catch (Exception e) {
|
||||
log.error("downloadFile error:{}", e.getMessage());
|
||||
response.setStatus(HttpStatus.ERROR);
|
||||
// 设置响应的 Content-Type 为文本
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
// 将错误信息写入响应体
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write("{\"error\": \"File download failed: " + e.getMessage() + "\"}");
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
return R.fail("传入参数不满足要求");
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件删除
|
||||
* 从各个存储平台删除文件
|
||||
* @param objectKey * @param objectKey,除mongodb 存fileid之外,其他均存上传文件的网络路径
|
||||
*/
|
||||
@DeleteMapping("/deleteFile")
|
||||
public AjaxResult deleteFile(@RequestParam("objectKey") String objectKey) {
|
||||
try {
|
||||
String fileUrl = Base64Utils.decodeUrl(objectKey);
|
||||
sysFileService.deleteFile(fileUrl);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
return AjaxResult.success("删除文件成功");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import java.io.InputStream;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
|
||||
import com.github.tobato.fastdfs.service.FastFileStorageClient;
|
||||
import com.bonus.common.core.utils.file.FileTypeUtils;
|
||||
|
||||
/**
|
||||
* FastDFS 文件存储
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class FastDfsSysFileServiceImpl implements ISysFileService
|
||||
{
|
||||
/**
|
||||
* 域名或本机访问地址
|
||||
*/
|
||||
@Value("${fdfs.domain}")
|
||||
public String domain;
|
||||
|
||||
@Autowired
|
||||
private FastFileStorageClient storageClient;
|
||||
|
||||
/**
|
||||
* FastDfs文件上传接口
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 访问地址
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public String uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
InputStream inputStream = file.getInputStream();
|
||||
StorePath storePath = storageClient.uploadFile(inputStream, file.getSize(),
|
||||
FileTypeUtils.getExtension(file), null);
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
return domain + "/" + storePath.getFullPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean downloadFile(String urlStr, String destination) throws Exception
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean deleteFile(String urlStr) throws Exception
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.system.api.domain.SysFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件上传接口
|
||||
*
|
||||
|
|
@ -16,17 +22,25 @@ public interface ISysFileService
|
|||
* @return 访问地址
|
||||
* @throws Exception
|
||||
*/
|
||||
public String uploadFile(MultipartFile file) throws Exception;
|
||||
public SysFile uploadFile(MultipartFile file) throws Exception;
|
||||
|
||||
/**
|
||||
* 文件上传接口
|
||||
*
|
||||
* @param files 上传的文件
|
||||
* @return 访问地址
|
||||
* @throws Exception
|
||||
*/
|
||||
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception;
|
||||
|
||||
/**
|
||||
* 从给定的URL下载文件并将其保存到指定的目标位置。
|
||||
*
|
||||
* @param urlStr 要下载文件的URL地址。
|
||||
* @param destination 文件下载后保存的本地文件系统路径。
|
||||
* @return 布尔值,指示下载是否成功。成功返回true,失败返回false。
|
||||
* @param response 请求响应。
|
||||
* @throws Exception 如果在下载过程中遇到任何异常,例如网络问题、文件写入问题等。
|
||||
*/
|
||||
public boolean downloadFile(String urlStr, String destination) throws Exception;
|
||||
public void downloadFile(HttpServletResponse response, String urlStr) throws Exception;
|
||||
|
||||
/**
|
||||
* 删除指定URL所指向的文件。
|
||||
|
|
@ -35,6 +49,6 @@ public interface ISysFileService
|
|||
* @return 布尔值,指示文件删除是否成功。如果文件被成功删除,则返回true;否则返回false。
|
||||
* @throws Exception 如果在删除文件的过程中遇到错误,例如网络问题、文件不存在或没有删除权限等。
|
||||
*/
|
||||
public boolean deleteFile(String urlStr) throws Exception;
|
||||
public void deleteFile(String urlStr) throws Exception;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import com.bonus.file.utils.FileDownloadUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bonus.file.utils.FileUploadUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* 本地文件存储
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Primary
|
||||
@Service
|
||||
public class LocalSysFileServiceImpl implements ISysFileService
|
||||
{
|
||||
/**
|
||||
* 资源映射路径 前缀
|
||||
*/
|
||||
@Value("${file.prefix}")
|
||||
public String localFilePrefix;
|
||||
|
||||
/**
|
||||
* 域名或本机访问地址
|
||||
*/
|
||||
@Value("${file.domain}")
|
||||
public String domain;
|
||||
|
||||
/**
|
||||
* 上传文件存储在本地的根路径
|
||||
*/
|
||||
@Value("${file.path}")
|
||||
private String localFilePath;
|
||||
|
||||
/**
|
||||
* 本地文件上传接口
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 访问地址
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public String uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
String name = FileUploadUtils.upload(localFilePath, file);
|
||||
String url = domain + localFilePrefix + name;
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean downloadFile(String urlStr, String destination) throws Exception
|
||||
{
|
||||
return FileDownloadUtils.downloadFile(urlStr, destination);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean deleteFile(String urlStr) throws Exception
|
||||
{
|
||||
String regex = String.format("^(.*?%s)", localFilePrefix);
|
||||
String updatePath = urlStr.replaceFirst(regex, localFilePath);
|
||||
Path path = Paths.get(updatePath);
|
||||
if (Files.exists(path)){
|
||||
try {
|
||||
Files.deleteIfExists(path);
|
||||
}catch (IOException e){
|
||||
throw new Exception(e.getMessage(), e);
|
||||
}
|
||||
}else {
|
||||
throw new Exception("删除文件时文件不存在");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import java.io.InputStream;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import com.bonus.file.config.MinioConfig;
|
||||
import com.bonus.file.utils.FileUploadUtils;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.PutObjectArgs;
|
||||
|
||||
/**
|
||||
* Minio 文件存储
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class MinioSysFileServiceImpl implements ISysFileService
|
||||
{
|
||||
@Autowired
|
||||
private MinioConfig minioConfig;
|
||||
|
||||
@Autowired
|
||||
private MinioClient client;
|
||||
|
||||
/**
|
||||
* Minio文件上传接口
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 访问地址
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public String uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
String fileName = FileUploadUtils.extractFilename(file);
|
||||
InputStream inputStream = file.getInputStream();
|
||||
PutObjectArgs args = PutObjectArgs.builder()
|
||||
.bucket(minioConfig.getBucketName())
|
||||
.object(fileName)
|
||||
.stream(inputStream, file.getSize(), -1)
|
||||
.contentType(file.getContentType())
|
||||
.build();
|
||||
client.putObject(args);
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean downloadFile(String urlStr, String destination) throws Exception
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean deleteFile(String urlStr) throws Exception
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package com.bonus.file.service.impl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import com.bonus.file.service.ISysFileService;
|
||||
import com.bonus.file.utils.FileUploadUtils;
|
||||
import com.bonus.system.api.domain.SysFile;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
|
||||
import com.github.tobato.fastdfs.service.FastFileStorageClient;
|
||||
import com.bonus.common.core.utils.file.FileTypeUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* FastDFS 文件存储
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
@ConditionalOnProperty(name = "storage.type", havingValue = "fdfs")
|
||||
public class FastDfsSysFileServiceImpl implements ISysFileService
|
||||
{
|
||||
/**
|
||||
* 域名或本机访问地址
|
||||
*/
|
||||
@Value("${fdfs.domain}")
|
||||
public String domain;
|
||||
|
||||
@Autowired
|
||||
private FastFileStorageClient storageClient;
|
||||
|
||||
/**
|
||||
* FastDfs文件上传接口
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 访问地址
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public SysFile uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
String fileName = FileUploadUtils.extractFilename(file);
|
||||
InputStream inputStream = file.getInputStream();
|
||||
StorePath storePath = storageClient.uploadFile(inputStream, file.getSize(),
|
||||
FileTypeUtils.getExtension(file), null);
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
return SysFile.builder().url(domain + "/" + storePath.getFullPath()).name(fileName).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
|
||||
List<SysFile> sysFiles = new ArrayList<>();
|
||||
for (MultipartFile file : files) {
|
||||
SysFile sysFile = uploadFile(file);
|
||||
sysFiles.add(sysFile);
|
||||
}
|
||||
return sysFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* FastDfs文件下载接口,待测试
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param urlStr 文件URL地址
|
||||
* @return 是否下载成功
|
||||
*/
|
||||
@Override
|
||||
public void downloadFile(HttpServletResponse response, String urlStr) throws Exception
|
||||
{
|
||||
try {
|
||||
String path = urlStr.replace(domain + "/", "");
|
||||
StorePath storePath = StorePath.parseFromUrl(path);
|
||||
|
||||
// 下载文件并返回输入流
|
||||
byte[] fileData = storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), IOUtils::toByteArray);
|
||||
|
||||
// 设置响应头
|
||||
String encodedFileName = URLEncoder.encode(storePath.getPath(), StandardCharsets.UTF_8.toString());
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + storePath.getPath() + "\"");
|
||||
|
||||
// 将文件内容写入响应
|
||||
IOUtils.write(fileData, response.getOutputStream());
|
||||
response.flushBuffer();
|
||||
} catch (Exception e) {
|
||||
throw new Exception("Error occurred while downloading file from FastDfs" + urlStr, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FastDfs文件删除接口,待测试
|
||||
*
|
||||
* @param urlStr 文件URL地址
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public void deleteFile(String urlStr) throws Exception
|
||||
{
|
||||
try {
|
||||
// 从 URL 中提取文件路径
|
||||
String path = urlStr.replace(domain + "/", "");
|
||||
StorePath storePath = StorePath.parseFromUrl(path);
|
||||
|
||||
// 删除文件
|
||||
storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
|
||||
} catch (Exception e) {
|
||||
throw new Exception("Error occurred while deleting file from FastDfs" + urlStr, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,21 @@
|
|||
package com.bonus.file.utils;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.bonus.common.core.constant.HttpStatus;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
||||
/**
|
||||
* 。
|
||||
|
|
@ -20,37 +28,43 @@ public class FileDownloadUtils {
|
|||
private final static String HTTPS_START_STR = "https://";
|
||||
private final static String FTP_START_STR = "ftp:/";
|
||||
|
||||
public static boolean downloadFile(String urlStr, String destination) throws IOException {
|
||||
public static boolean downloadFile(HttpServletResponse response,String urlStr) throws IOException {
|
||||
if (urlStr.startsWith(HTTP_START_STR) || urlStr.startsWith(HTTPS_START_STR)) {
|
||||
return downloadHttpFile(urlStr, destination);
|
||||
return downloadHttpFile(response, urlStr);
|
||||
} else if (urlStr.startsWith(FTP_START_STR)) {
|
||||
return downloadFtpFile(urlStr, destination);
|
||||
return downloadFtpFile(response, urlStr);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported protocol: " + urlStr);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean downloadHttpFile(String urlStr, String destination) throws IOException {
|
||||
private static boolean downloadHttpFile(HttpServletResponse response, String urlStr) throws IOException {
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
com.bonus.file.utils.FileUtils.setResponseHeaderByUrl(response, urlStr);
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream();
|
||||
FileOutputStream outputStream = new FileOutputStream(destination)) {
|
||||
ServletOutputStream outputStream = response.getOutputStream()) {
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead = -1;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
System.out.println("HTTP download completed: " + destination);
|
||||
} finally {
|
||||
System.out.println("HTTP download completed: ");
|
||||
} catch (Exception e) {
|
||||
throw new IOException("没有找到文件");
|
||||
}
|
||||
|
||||
finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean downloadFtpFile(String urlStr, String destination) throws IOException {
|
||||
private static boolean downloadFtpFile(HttpServletResponse response, String urlStr) throws IOException{
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
try {
|
||||
URL url = new URL(urlStr);
|
||||
|
|
@ -67,8 +81,10 @@ public class FileDownloadUtils {
|
|||
ftpClient.enterLocalPassiveMode();
|
||||
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
|
||||
com.bonus.file.utils.FileUtils.setResponseHeaderByUrl(response, urlStr);
|
||||
|
||||
try (InputStream inputStream = ftpClient.retrieveFileStream(remoteFilePath);
|
||||
FileOutputStream outputStream = new FileOutputStream(destination)) {
|
||||
ServletOutputStream outputStream = response.getOutputStream()) {
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead = -1;
|
||||
|
|
@ -77,11 +93,13 @@ public class FileDownloadUtils {
|
|||
}
|
||||
|
||||
if (ftpClient.completePendingCommand()) {
|
||||
System.out.println("FTP download completed: " + destination);
|
||||
System.out.println("FTP download completed: ");
|
||||
} else {
|
||||
System.out.println("Failed to complete FTP download: " + destination);
|
||||
System.out.println("Failed to complete FTP download: ");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
|
|
@ -89,27 +107,62 @@ public class FileDownloadUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
// public static String replaceUrlPrefix(String originalUrl, String newPrefix) {
|
||||
// // 使用正则表达式匹配 URL 中的 "statics" 及其之前的部分
|
||||
// String regex = "^(.*?/statics)";
|
||||
// return originalUrl.replaceFirst(regex, newPrefix);
|
||||
// }
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// try {
|
||||
// // 示例下载文件
|
||||
// String httpUrl = "http://example.com/file.txt";
|
||||
// String ftpUrl = "ftp://username:password@ftp.example.com/file.txt";
|
||||
// String destination = "downloaded_file.txt";
|
||||
// private static boolean downloadHttpFile(String urlStr, String destination) throws IOException {
|
||||
// URL url = new URL(urlStr);
|
||||
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
// connection.setRequestMethod("GET");
|
||||
//
|
||||
// // 下载HTTP文件
|
||||
// downloadFile(httpUrl, destination);
|
||||
// try (InputStream inputStream = connection.getInputStream();
|
||||
// FileOutputStream outputStream = new FileOutputStream(destination)) {
|
||||
//
|
||||
// // 下载FTP文件
|
||||
// downloadFile(ftpUrl, destination);
|
||||
//
|
||||
// } catch (IOException ex) {
|
||||
// ex.printStackTrace();
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead = -1;
|
||||
// while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
// outputStream.write(buffer, 0, bytesRead);
|
||||
// }
|
||||
// System.out.println("HTTP download completed: " + destination);
|
||||
// } finally {
|
||||
// connection.disconnect();
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// private static boolean downloadFtpFile(String urlStr, String destination) throws IOException {
|
||||
// FTPClient ftpClient = new FTPClient();
|
||||
// try {
|
||||
// URL url = new URL(urlStr);
|
||||
// String host = url.getHost();
|
||||
// int port = url.getPort() == -1 ? 21 : url.getPort();
|
||||
// String userInfo = url.getUserInfo();
|
||||
// String[] credentials = userInfo != null ? userInfo.split(":") : new String[]{"anonymous", ""};
|
||||
// String username = credentials[0];
|
||||
// String password = credentials.length > 1 ? credentials[1] : "";
|
||||
// String remoteFilePath = url.getPath();
|
||||
//
|
||||
// ftpClient.connect(host, port);
|
||||
// ftpClient.login(username, password);
|
||||
// ftpClient.enterLocalPassiveMode();
|
||||
// ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
//
|
||||
// try (InputStream inputStream = ftpClient.retrieveFileStream(remoteFilePath);
|
||||
// FileOutputStream outputStream = new FileOutputStream(destination)) {
|
||||
//
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead = -1;
|
||||
// while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
// outputStream.write(buffer, 0, bytesRead);
|
||||
// }
|
||||
//
|
||||
// if (ftpClient.completePendingCommand()) {
|
||||
// System.out.println("FTP download completed: " + destination);
|
||||
// } else {
|
||||
// System.out.println("Failed to complete FTP download: " + destination);
|
||||
// }
|
||||
// }
|
||||
// } finally {
|
||||
// ftpClient.logout();
|
||||
// ftpClient.disconnect();
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package com.bonus.file.utils;
|
||||
|
||||
import com.bonus.common.core.constant.HttpStatus;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class FileUtils {
|
||||
/**
|
||||
* 获取文件名
|
||||
*/
|
||||
public static String setResponseHeaderByUrl(HttpServletResponse response, String url) {
|
||||
String safeFileName = com.bonus.common.core.utils.file.FileUtils.getName(url);
|
||||
if (!StringUtils.hasText(safeFileName)) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8.toString());
|
||||
if (encodedFileName == null) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST);
|
||||
return null;
|
||||
}
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"");
|
||||
return encodedFileName;
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,28 @@
|
|||
package com.bonus.obs.utils;
|
||||
package com.bonus.file.utils;
|
||||
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.obs.config.ObsConfig;
|
||||
import com.bonus.obs.domain.ObsInfo;
|
||||
import com.bonus.file.config.ObsConfig;
|
||||
import com.bonus.system.api.domain.SysFile;
|
||||
import com.obs.services.ObsClient;
|
||||
import com.obs.services.model.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.io.*;
|
||||
import java.net.URLConnection;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
@ConditionalOnProperty(name = "storage.type", havingValue = "obs")
|
||||
public class ObsUtils {
|
||||
|
||||
@Resource
|
||||
|
|
@ -45,10 +48,10 @@ public class ObsUtils {
|
|||
* @param file 要上传的文件
|
||||
* @return 上传结果
|
||||
*/
|
||||
public ObsInfo uploadFile(String objectKey, File file) {
|
||||
public SysFile uploadFile(String objectKey, File file) {
|
||||
if (file.length() < 100 * 1024 * 1024L) {
|
||||
obsClient.putObject(obsConfig.getBucket(), objectKey, file);
|
||||
return ObsInfo.builder().bucketName(obsConfig.getBucket()).name(FileUtils.getName(objectKey)).fileType(FileUtils.getName(objectKey).substring(FileUtils.getName(objectKey).lastIndexOf('.')).toLowerCase()).length(file.length()).path(objectKey).build();
|
||||
return SysFile.builder().url(objectKey).build();
|
||||
} else {
|
||||
return multipartUploadFile(objectKey, file);
|
||||
}
|
||||
|
|
@ -62,7 +65,7 @@ public class ObsUtils {
|
|||
* @param file 要上传的文件
|
||||
* @return 上传结果
|
||||
*/
|
||||
public ObsInfo multipartUploadFile(String objectKey, File file) {
|
||||
public SysFile multipartUploadFile(String objectKey, File file) {
|
||||
// 创建一个固定大小为10的线程池
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(10);
|
||||
try {
|
||||
|
|
@ -84,7 +87,7 @@ public class ObsUtils {
|
|||
final int partNumber = i + 1;
|
||||
final long offset = partSize * i;
|
||||
final long size = Math.min(partSize, fileLength - offset);
|
||||
Future<UploadPartResult> future = executorService.submit(() -> {
|
||||
Future<com.obs.services.model.UploadPartResult> future = executorService.submit(() -> {
|
||||
UploadPartRequest uploadPartRequest = new UploadPartRequest(
|
||||
obsConfig.getBucket(),
|
||||
objectKey,
|
||||
|
|
@ -100,8 +103,8 @@ public class ObsUtils {
|
|||
futures.add(future);
|
||||
}
|
||||
|
||||
for (Future<UploadPartResult> future : futures) {
|
||||
UploadPartResult uploadPartResult = future.get();
|
||||
for (Future<com.obs.services.model.UploadPartResult> future : futures) {
|
||||
com.obs.services.model.UploadPartResult uploadPartResult = future.get();
|
||||
partETags.add(new PartEtag(uploadPartResult.getEtag(), uploadPartResult.getPartNumber()));
|
||||
}
|
||||
|
||||
|
|
@ -117,13 +120,7 @@ public class ObsUtils {
|
|||
);
|
||||
obsClient.completeMultipartUpload(completeRequest);
|
||||
|
||||
return ObsInfo.builder()
|
||||
.bucketName(obsConfig.getBucket())
|
||||
.name(FileUtils.getName(objectKey))
|
||||
.fileType(FileUtils.getName(objectKey).substring(FileUtils.getName(objectKey).lastIndexOf('.')).toLowerCase())
|
||||
.length(file.length())
|
||||
.path(objectKey)
|
||||
.build();
|
||||
return SysFile.builder().url(objectKey).build();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
|
|
@ -139,7 +136,7 @@ public class ObsUtils {
|
|||
* @param objectKey 文件在OBS中的键
|
||||
*/
|
||||
public R<ObsObject> downloadFile(String objectKey) {
|
||||
if (!doesObjectExist(objectKey)) {
|
||||
if (doesObjectExist(objectKey)) {
|
||||
return R.fail("文件不存在");
|
||||
}
|
||||
return R.ok(obsClient.getObject(new GetObjectRequest(obsConfig.getBucket(), objectKey)));
|
||||
|
|
@ -149,12 +146,14 @@ public class ObsUtils {
|
|||
* 删除文件从OBS
|
||||
*
|
||||
* @param objectKey 文件在OBS中的键
|
||||
* @return 如果删除成功则返回true,否则返回false
|
||||
*/
|
||||
public R<DeleteObjectResult> deleteFile(String objectKey) {
|
||||
if (!doesObjectExist(objectKey)) {
|
||||
return R.fail("文件不存在");
|
||||
public boolean deleteFile(String objectKey) throws Exception {
|
||||
if (doesObjectExist(objectKey)) {
|
||||
throw new Exception("文件不存在");
|
||||
}
|
||||
return R.ok(null, "文件删除成功");
|
||||
obsClient.deleteObject(obsConfig.getBucket(), objectKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,9 +164,9 @@ public class ObsUtils {
|
|||
*/
|
||||
public boolean doesObjectExist(String objectKey) {
|
||||
try {
|
||||
return obsClient.doesObjectExist(obsConfig.getBucket(), objectKey);
|
||||
return !obsClient.doesObjectExist(obsConfig.getBucket(), objectKey);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.bonus.oss.utils;
|
||||
package com.bonus.file.utils;
|
||||
|
||||
import com.aliyun.oss.OSS;
|
||||
import com.aliyun.oss.OSSClientBuilder;
|
||||
|
|
@ -6,11 +6,12 @@ import com.aliyun.oss.model.*;
|
|||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.text.Convert;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.oss.config.OSSConfig;
|
||||
import com.bonus.oss.domain.OssInfo;
|
||||
import com.bonus.file.config.OSSConfig;
|
||||
import com.bonus.system.api.domain.SysFile;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
|
@ -19,6 +20,8 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import com.bonus.system.api.domain.SysFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -29,6 +32,7 @@ import java.util.concurrent.Future;
|
|||
* @author jiang
|
||||
*/
|
||||
@Service
|
||||
@ConditionalOnProperty(name = "storage.type", havingValue = "oss")
|
||||
public class OssUtils {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(OssUtils.class);
|
||||
|
|
@ -56,7 +60,7 @@ public class OssUtils {
|
|||
* @param file 要上传的文件
|
||||
* @return 包含上传文件信息的结果对象
|
||||
*/
|
||||
public R<OssInfo> upload(String objectKey, File file) {
|
||||
public SysFile upload(String objectKey, File file) throws Exception {
|
||||
try {
|
||||
if (file.length() < 10 * 1024 * 1024L) {
|
||||
ossClient.putObject(ossConfig.getBucket(), objectKey, file);
|
||||
|
|
@ -64,10 +68,10 @@ public class OssUtils {
|
|||
ossMultipartParallelUpload(objectKey, file);
|
||||
}
|
||||
|
||||
return R.ok(getInfo(objectKey), "文件上传成功");
|
||||
return getInfo(objectKey);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("文件上传失败", e);
|
||||
return R.fail("文件上传失败");
|
||||
return null;
|
||||
} finally {
|
||||
if (!file.delete()) {
|
||||
LOGGER.warn("临时文件删除失败");
|
||||
|
|
@ -151,18 +155,15 @@ public class OssUtils {
|
|||
* @param objectKey 文件路径
|
||||
* @return 文件信息对象
|
||||
*/
|
||||
public OssInfo getInfo(String objectKey) {
|
||||
public SysFile getInfo(String objectKey) {
|
||||
try {
|
||||
ObjectMetadata objectMetadata = ossClient.getObjectMetadata(ossConfig.getBucket(), objectKey);
|
||||
return OssInfo.builder()
|
||||
return SysFile.builder()
|
||||
.name(FileUtils.getName(objectKey))
|
||||
.bucketName(ossConfig.getBucket())
|
||||
.fileType(FileUtils.getName(objectKey).substring(FileUtils.getName(objectKey).lastIndexOf('.')).toLowerCase())
|
||||
.length(Convert.toStr(objectMetadata.getContentLength()))
|
||||
.path(objectKey).build();
|
||||
.url(objectKey).build();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("获取文件信息失败", e);
|
||||
return OssInfo.builder().build();
|
||||
return SysFile.builder().build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -172,15 +173,15 @@ public class OssUtils {
|
|||
* @param objectKey 文件路径
|
||||
* @return OSSObject对象,包含下载的文件
|
||||
*/
|
||||
public R<OSSObject> download(String objectKey) {
|
||||
public R<OSSObject> download(String objectKey) throws Exception {
|
||||
if (doesObjectExist(ossConfig.getBucket(), objectKey)) {
|
||||
return R.fail("文件不存在");
|
||||
throw new Exception("文件不存在");
|
||||
}
|
||||
try {
|
||||
return R.ok(ossClient.getObject(ossConfig.getBucket(), objectKey));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("文件下载失败", e);
|
||||
return R.fail("文件下载失败");
|
||||
throw new Exception("文件下载失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,16 +191,16 @@ public class OssUtils {
|
|||
* @param objectKey 文件路径
|
||||
* @return 删除操作的结果
|
||||
*/
|
||||
public R<T> delete(String objectKey) {
|
||||
public boolean delete(String objectKey) throws Exception {
|
||||
try {
|
||||
if (doesObjectExist(ossConfig.getBucket(), objectKey)) {
|
||||
return R.fail("文件不存在");
|
||||
throw new Exception("删除文件时文件不存在");
|
||||
}
|
||||
ossClient.deleteObject(ossConfig.getBucket(), objectKey);
|
||||
return R.ok(null, "文件删除成功");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("文件删除失败", e);
|
||||
return R.fail("文件删除失败");
|
||||
throw new Exception("删除文件时文件删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -210,12 +211,11 @@ public class OssUtils {
|
|||
* @param objectKey 文件路径
|
||||
* @return 如果文件存在返回true,否则返回false
|
||||
*/
|
||||
private boolean doesObjectExist(String bucketName, String objectKey) {
|
||||
private boolean doesObjectExist(String bucketName, String objectKey) throws Exception {
|
||||
try {
|
||||
return !ossClient.doesObjectExist(bucketName, objectKey);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("判断文件是否存在失败", e);
|
||||
return true;
|
||||
throw new Exception(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-modules</artifactId>
|
||||
<version>24.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>bonus-modules-mongodb</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FastDFS -->
|
||||
<dependency>
|
||||
<groupId>com.github.tobato</groupId>
|
||||
<artifactId>fastdfs-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Api System -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Common Swagger -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-swagger</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.12</version> <!-- 版本号可以根据需要调整 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.bonus.mongodb;
|
||||
|
||||
import com.bonus.common.swagger.annotation.EnableCustomSwagger2;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@EnableCustomSwagger2
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||
public class BonusMongodbApplication {
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(BonusMongodbApplication.class, args);
|
||||
System.out.println("(♥◠‿◠)ノ゙ MongoDB存储服务模块启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||
" .-------. ____ __ \n" +
|
||||
" | _ _ \\ \\ \\ / / \n" +
|
||||
" | ( ' ) | \\ _. / ' \n" +
|
||||
" |(_ o _) / _( )_ .' \n" +
|
||||
" | (_,_).' __ ___(_ o _)' \n" +
|
||||
" | |\\ \\ | || |(_,_)' \n" +
|
||||
" | | \\ `' /| `-' / \n" +
|
||||
" | | \\ / \\ / \n" +
|
||||
" ''-' `'-' `-..-' ");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package com.bonus.mongodb.config;
|
||||
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.gridfs.GridFSBucket;
|
||||
import com.mongodb.client.gridfs.GridFSBuckets;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
|
||||
/**
|
||||
* @author coisini
|
||||
* @version 1.0
|
||||
* @Description MongoDB配置类
|
||||
* @date Apr 17, 2022
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "spring.data.mongodb")
|
||||
@Data
|
||||
public class MongoConfig {
|
||||
/**
|
||||
* 数据库配置信息
|
||||
*/
|
||||
private String database;
|
||||
private String host;
|
||||
private Integer port;
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
/**移除静态修饰符,避免单例模式
|
||||
*
|
||||
*/
|
||||
private MongoClient mongoClient;
|
||||
|
||||
/** 使用@Bean注解,并在方法内部进行异常处理
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MongoClient getMongoClient() {
|
||||
if (mongoClient == null) {
|
||||
try {
|
||||
mongoClient = MongoClients.create(String.format("mongodb://%s:%s@%s:%d/%s", URLEncoder.encode(username, "UTF-8"),URLEncoder.encode(password, "UTF-8"), host, port, database));
|
||||
} catch (Exception e) {
|
||||
// 在实际应用中应该有更详细的异常处理策略,例如记录日志并抛出自定义异常
|
||||
throw new RuntimeException("Failed to create MongoClient", e);
|
||||
}
|
||||
}
|
||||
return mongoClient;
|
||||
}
|
||||
|
||||
/** 直接返回MongoDatabase实例,依赖注入会处理实例的获取
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public MongoDatabase getMongoDatabase() {
|
||||
return getMongoClient().getDatabase(database);
|
||||
}
|
||||
|
||||
/** 创建GridFSBucket的@Bean方法,依赖注入MongoDatabase
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public GridFSBucket getGridFsBucket() {
|
||||
return GridFSBuckets.create(getMongoDatabase());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
package com.bonus.mongodb.controller;
|
||||
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.mongodb.damain.FileExportVo;
|
||||
import com.bonus.mongodb.service.MongodbService;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mongodb/")
|
||||
@Slf4j
|
||||
public class MongodbController {
|
||||
|
||||
@Resource
|
||||
private MongodbService mongodbService;
|
||||
|
||||
@ApiOperation(value = "单文件上传")
|
||||
@PostMapping("uploadFile")
|
||||
public R<FileExportVo> uploadFile(MultipartFile file) {
|
||||
try {
|
||||
FileExportVo fileExportVo = mongodbService.uploadFile(file);
|
||||
return R.ok(fileExportVo);
|
||||
} catch (Exception e) {
|
||||
log.error("单文件上传", e);
|
||||
return R.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "多文件上传")
|
||||
@PostMapping("uploadFiles")
|
||||
public R<List<FileExportVo>> uploadFiles(MultipartFile[] files) {
|
||||
try {
|
||||
List<MultipartFile> multipartFiles = Arrays.asList(files);
|
||||
List<FileExportVo> list = mongodbService.uploadFiles(multipartFiles);
|
||||
return R.ok(list);
|
||||
} catch (Exception e) {
|
||||
log.error("多文件上传", e);
|
||||
return R.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "文件删除")
|
||||
@PostMapping("delFile")
|
||||
public R<T> delFile(String fileId) {
|
||||
try {
|
||||
mongodbService.removeFile(fileId);
|
||||
return R.ok();
|
||||
} catch (Exception e) {
|
||||
log.error("文件删除", e);
|
||||
return R.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取文件base64")
|
||||
@PostMapping("getFileBase64")
|
||||
public R<byte[]> getFileBase64(String fileId) {
|
||||
try {
|
||||
FileExportVo fileExportVo = mongodbService.downloadFile(fileId);
|
||||
return R.ok(fileExportVo.getData());
|
||||
} catch (Exception e) {
|
||||
log.error("获取文件base64", e);
|
||||
return R.fail();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
package com.bonus.mongodb.damain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@Data
|
||||
public class FileExportVo implements Serializable {
|
||||
|
||||
private String file;
|
||||
|
||||
private String fileId;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private String suffix;
|
||||
|
||||
private long fileSize;
|
||||
|
||||
private String sourceId;
|
||||
|
||||
private String id;
|
||||
|
||||
private String fileType;
|
||||
/**
|
||||
* 资源类型
|
||||
*/
|
||||
private String sourceType;
|
||||
|
||||
private String updaetTime;
|
||||
|
||||
private byte[] data;
|
||||
|
||||
public FileExportVo(MongoFile mongoFile) {
|
||||
BeanUtil.copyProperties(mongoFile, this);
|
||||
if (Objects.nonNull(mongoFile.getContent())) {
|
||||
this.data = mongoFile.getContent().getData();
|
||||
}
|
||||
this.fileId = mongoFile.getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package com.bonus.mongodb.damain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.bson.types.Binary;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
@Document(collection = "files")
|
||||
public class MongoFile {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@Id
|
||||
public String id;
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
public String fileName;
|
||||
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
public long fileSize;
|
||||
|
||||
/**
|
||||
* 上传时间
|
||||
*/
|
||||
public String uploadDate;
|
||||
|
||||
/**
|
||||
* MD5值
|
||||
*/
|
||||
public String md5;
|
||||
|
||||
/**
|
||||
* 文件内容
|
||||
*/
|
||||
private Binary content;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
public String contentType;
|
||||
|
||||
/**
|
||||
* 文件后缀名
|
||||
*/
|
||||
public String suffix;
|
||||
|
||||
/**
|
||||
* 文件描述
|
||||
*/
|
||||
public String description;
|
||||
|
||||
/**
|
||||
* 大文件管理GridFS的ID
|
||||
*/
|
||||
private String gridFsId;
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.bonus.mongodb.repository;
|
||||
|
||||
import com.bonus.mongodb.damain.MongoFile;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
public interface MongoFileRepository extends MongoRepository<MongoFile, String> {
|
||||
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package com.bonus.mongodb.service;
|
||||
|
||||
import com.bonus.mongodb.damain.FileExportVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
public interface MongodbService {
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file
|
||||
* @return FileExportVo
|
||||
* @throws Exception
|
||||
* @description
|
||||
* @date 2024/3/11 15:44
|
||||
*/
|
||||
FileExportVo uploadFile(MultipartFile file) throws Exception;
|
||||
|
||||
/**
|
||||
* 多文件上传
|
||||
*
|
||||
* @param files
|
||||
* @return
|
||||
*/
|
||||
List<FileExportVo> uploadFiles(List<MultipartFile> files);
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param fileId
|
||||
* @return
|
||||
*/
|
||||
FileExportVo downloadFile(String fileId);
|
||||
|
||||
/**
|
||||
* 文件删除
|
||||
*
|
||||
* @param fileId
|
||||
*/
|
||||
void removeFile(String fileId);
|
||||
}
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
package com.bonus.mongodb.service.impl;
|
||||
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.bonus.common.core.utils.DateUtils;
|
||||
import com.bonus.mongodb.damain.FileExportVo;
|
||||
import com.bonus.mongodb.damain.MongoFile;
|
||||
import com.bonus.mongodb.repository.MongoFileRepository;
|
||||
import com.bonus.mongodb.service.MongodbService;
|
||||
import com.bonus.mongodb.utils.Md5Util;
|
||||
import com.mongodb.client.gridfs.GridFSBucket;
|
||||
import com.mongodb.client.gridfs.GridFSDownloadStream;
|
||||
import com.mongodb.client.gridfs.model.GridFSFile;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bson.types.Binary;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.gridfs.GridFsResource;
|
||||
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class MongodbServiceImpl implements MongodbService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MongodbServiceImpl.class);
|
||||
private final MongoFileRepository mongoFileRepository;
|
||||
private final MongoTemplate mongoTemplate;
|
||||
private final GridFsTemplate gridFsTemplate;
|
||||
private final GridFSBucket gridFSBucket;
|
||||
|
||||
private static final int MAX_SIZE = 16777216;
|
||||
private static final String SUFFIX = ".";
|
||||
|
||||
|
||||
/**
|
||||
* 多文件上传
|
||||
*
|
||||
* @param files
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<FileExportVo> uploadFiles(List<MultipartFile> files) {
|
||||
|
||||
return files.stream().map(file -> {
|
||||
try {
|
||||
return this.uploadFile(file);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public FileExportVo uploadFile(MultipartFile file) throws Exception {
|
||||
if (file.getSize() > MAX_SIZE) {
|
||||
return this.saveGridFsFile(file);
|
||||
} else {
|
||||
return this.saveBinaryFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param fileId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public FileExportVo downloadFile(String fileId) {
|
||||
Optional<MongoFile> option = this.getBinaryFileById(fileId);
|
||||
|
||||
if (option.isPresent()) {
|
||||
MongoFile mongoFile = option.get();
|
||||
if (Objects.isNull(mongoFile.getContent())) {
|
||||
option = this.getGridFsFileById(fileId);
|
||||
}
|
||||
}
|
||||
|
||||
return option.map(FileExportVo::new).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件删除
|
||||
*
|
||||
* @param fileId
|
||||
*/
|
||||
@Override
|
||||
public void removeFile(String fileId) {
|
||||
Optional<MongoFile> option = this.getBinaryFileById(fileId);
|
||||
|
||||
if (option.isPresent()) {
|
||||
if (Objects.nonNull(option.get().getGridFsId())) {
|
||||
this.removeGridFsFile(fileId);
|
||||
} else {
|
||||
this.removeBinaryFile(fileId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Binary文件
|
||||
*
|
||||
* @param fileId
|
||||
*/
|
||||
public void removeBinaryFile(String fileId) {
|
||||
mongoFileRepository.deleteById(fileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除GridFs文件
|
||||
*
|
||||
* @param fileId
|
||||
*/
|
||||
public void removeGridFsFile(String fileId) {
|
||||
// TODO 根据id查询文件
|
||||
MongoFile mongoFile = mongoTemplate.findById(fileId, MongoFile.class);
|
||||
if (Objects.nonNull(mongoFile)) {
|
||||
// TODO 根据文件ID删除fs.files和fs.chunks中的记录
|
||||
Query deleteFileQuery = new Query().addCriteria(Criteria.where("filename").is(mongoFile.getGridFsId()));
|
||||
gridFsTemplate.delete(deleteFileQuery);
|
||||
// TODO 删除集合mongoFile中的数据
|
||||
Query deleteQuery = new Query(Criteria.where("id").is(fileId));
|
||||
mongoTemplate.remove(deleteQuery, MongoFile.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存Binary文件(小文件)
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public FileExportVo saveBinaryFile(MultipartFile file) throws Exception {
|
||||
|
||||
String suffix = getFileSuffix(file);
|
||||
MongoFile mongoFile = mongoFileRepository.save(
|
||||
MongoFile.builder()
|
||||
.fileName(file.getOriginalFilename())
|
||||
.fileSize(file.getSize())
|
||||
.content(new Binary(file.getBytes()))
|
||||
.contentType(file.getContentType())
|
||||
.uploadDate(DateUtils.getTime())
|
||||
.suffix(suffix)
|
||||
.md5(Md5Util.getMd5(file.getInputStream()))
|
||||
.build()
|
||||
);
|
||||
|
||||
return new FileExportVo(mongoFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存GridFs文件(大文件)
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public FileExportVo saveGridFsFile(MultipartFile file) throws Exception {
|
||||
String suffix = getFileSuffix(file);
|
||||
|
||||
String gridFsId = this.storeFileToGridFs(file.getInputStream(), file.getContentType());
|
||||
|
||||
MongoFile mongoFile = mongoTemplate.save(
|
||||
MongoFile.builder()
|
||||
.fileName(file.getOriginalFilename())
|
||||
.fileSize(file.getSize())
|
||||
.contentType(file.getContentType())
|
||||
.uploadDate(DateUtils.getTime())
|
||||
.suffix(suffix)
|
||||
.md5(Md5Util.getMd5(file.getInputStream()))
|
||||
.gridFsId(gridFsId)
|
||||
.build()
|
||||
);
|
||||
|
||||
return new FileExportVo(mongoFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到Mongodb的GridFs中
|
||||
*
|
||||
* @param in
|
||||
* @param contentType
|
||||
* @return
|
||||
*/
|
||||
public String storeFileToGridFs(InputStream in, String contentType) throws IOException {
|
||||
try {
|
||||
String gridFsId = IdUtil.simpleUUID();
|
||||
// TODO 将文件存储进GridFS中
|
||||
gridFsTemplate.store(in, gridFsId, contentType);
|
||||
return gridFsId;
|
||||
}catch (Exception e){
|
||||
log.error("上传文件到Mongodb的GridFs中失败",e);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Binary文件
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public Optional<MongoFile> getBinaryFileById(String id) {
|
||||
return mongoFileRepository.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Grid文件
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public Optional<MongoFile> getGridFsFileById(String id) {
|
||||
MongoFile mongoFile = mongoTemplate.findById(id, MongoFile.class);
|
||||
if (Objects.nonNull(mongoFile)) {
|
||||
Query gridQuery = new Query().addCriteria(Criteria.where("filename").is(mongoFile.getGridFsId()));
|
||||
try {
|
||||
// TODO 根据id查询文件
|
||||
GridFSFile fsFile = gridFsTemplate.findOne(gridQuery);
|
||||
// TODO 打开流下载对象
|
||||
GridFSDownloadStream in = gridFSBucket.openDownloadStream(fsFile.getObjectId());
|
||||
if (in.getGridFSFile().getLength() > 0) {
|
||||
// TODO 获取流对象
|
||||
GridFsResource resource = new GridFsResource(fsFile, in);
|
||||
// TODO 获取数据
|
||||
mongoFile.setContent(new Binary(IoUtil.readBytes(resource.getInputStream())));
|
||||
return Optional.of(mongoFile);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件后缀
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
private String getFileSuffix(MultipartFile file) {
|
||||
String suffix = "";
|
||||
if (Objects.requireNonNull(file.getOriginalFilename()).contains(SUFFIX)) {
|
||||
suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
|
||||
}
|
||||
return suffix;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.bonus.mongodb.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @author 10488
|
||||
* @version 1.0
|
||||
* @Description MD5工具类
|
||||
* @date Apr 8, 2022
|
||||
*/
|
||||
public class Md5Util {
|
||||
/**
|
||||
* 获取该输入流的MD5值
|
||||
*/
|
||||
public static String getMd5(InputStream is) throws NoSuchAlgorithmException, IOException {
|
||||
StringBuffer md5 = new StringBuffer();
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] dataBytes = new byte[1024];
|
||||
|
||||
int nread = 0;
|
||||
while ((nread = is.read(dataBytes)) != -1) {
|
||||
md.update(dataBytes, 0, nread);
|
||||
}
|
||||
;
|
||||
byte[] mdbytes = md.digest();
|
||||
|
||||
// convert the byte to hex format
|
||||
for (int i = 0; i < mdbytes.length; i++) {
|
||||
md5.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
is.close();
|
||||
return md5.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 9206
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 5GB
|
||||
max-request-size: 5GB
|
||||
application:
|
||||
# 应用名称
|
||||
name: bonus-mongodb
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: dev
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 192.168.0.56:8848
|
||||
namespace: f648524d-0a7b-449e-8f92-64e05236fd51
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 192.168.0.56:8848
|
||||
namespace: f648524d-0a7b-449e-8f92-64e05236fd51
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/bonus-file" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.bonus" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
</configuration>
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-modules</artifactId>
|
||||
<version>24.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>bonus-modules-obs</artifactId>
|
||||
<description>
|
||||
bonus-modules-obs存储服务
|
||||
</description>
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FastDFS -->
|
||||
<dependency>
|
||||
<groupId>com.github.tobato</groupId>
|
||||
<artifactId>fastdfs-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Api System -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Common Swagger -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-swagger</artifactId>
|
||||
</dependency>
|
||||
<!-- huaweicloud obs -->
|
||||
<dependency>
|
||||
<groupId>com.huaweicloud</groupId>
|
||||
<artifactId>esdk-obs-java-bundle</artifactId>
|
||||
<version>3.23.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.bonus.obs;
|
||||
|
||||
import com.bonus.common.swagger.annotation.EnableCustomSwagger2;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@EnableCustomSwagger2
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||
public class BonusObsApplication {
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(BonusObsApplication.class, args);
|
||||
System.out.println("(♥◠‿◠)ノ゙ obs存储服务模块启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||
" .-------. ____ __ \n" +
|
||||
" | _ _ \\ \\ \\ / / \n" +
|
||||
" | ( ' ) | \\ _. / ' \n" +
|
||||
" |(_ o _) / _( )_ .' \n" +
|
||||
" | (_,_).' __ ___(_ o _)' \n" +
|
||||
" | |\\ \\ | || |(_,_)' \n" +
|
||||
" | | \\ `' /| `-' / \n" +
|
||||
" | | \\ / \\ / \n" +
|
||||
" ''-' `'-' `-..-' ");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
package com.bonus.obs.controller;
|
||||
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.obs.domain.ObsInfo;
|
||||
import com.bonus.obs.service.ObsService;
|
||||
import com.obs.services.model.DeleteObjectResult;
|
||||
import com.obs.services.model.ObsObject;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/obs")
|
||||
public class ObsController {
|
||||
@Resource
|
||||
private ObsService service;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@PostMapping("/uploadFile")
|
||||
public R<ObsInfo> uploadFile(MultipartFile file) {
|
||||
return service.uploadFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件从OBS
|
||||
*
|
||||
* @param objectKey 文件在OBS中的键
|
||||
*/
|
||||
@PostMapping("/deleteFile")
|
||||
public R<DeleteObjectResult> deleteFile(String objectKey) {
|
||||
return service.deleteFile(objectKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param objectKey obs文件存储地址
|
||||
*/
|
||||
@GetMapping("/downloadFile")
|
||||
public void downloadFile(HttpServletResponse response, @RequestParam String objectKey) {
|
||||
R<ObsObject> obsObjectR = service.downloadFile(objectKey);
|
||||
try {
|
||||
if (R.isError(obsObjectR)) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
return;
|
||||
}
|
||||
if (obsObjectR.getData() == null) {
|
||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||
return;
|
||||
}
|
||||
InputStream inputStream = obsObjectR.getData().getObjectContent();
|
||||
String safeFileName = FileUtils.getFileNameFromPath(objectKey);
|
||||
String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8.toString());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"");
|
||||
try (OutputStream outputStream = response.getOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
outputStream.flush();
|
||||
} catch (IOException e) {
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
} catch (Exception e) {
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param files 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@PostMapping("/uploadFiles")
|
||||
public R<List<ObsInfo>> uploadFiles(MultipartFile[] files) {
|
||||
return service.uploadFiles(files);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package com.bonus.obs.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* OBS(Object Storage Service)信息类。
|
||||
* 用于封装与OBS对象相关的元数据信息。
|
||||
* @author jiang
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ObsInfo {
|
||||
/**
|
||||
* 对象的名称。
|
||||
* 代表OBS对象的唯一标识。
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 对象在OBS中的存储路径。
|
||||
* 包含桶(bucket)名称和对象在桶内的相对路径。
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 对象的大小。
|
||||
* 以字符串形式表示,可能包含单位(如字节、KB、MB等)。
|
||||
*/
|
||||
private Long length;
|
||||
|
||||
/**
|
||||
* 对象的文件类型。
|
||||
* 用于标识对象的MIME类型,例如text/plain、image/jpeg等。
|
||||
*/
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 对象所属的桶(bucket)的名称。
|
||||
* 每个桶在OBS中都是唯一的,用于存储对象。
|
||||
*/
|
||||
private String bucketName;
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.bonus.obs.service;
|
||||
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.obs.domain.ObsInfo;
|
||||
import com.obs.services.model.DeleteObjectResult;
|
||||
import com.obs.services.model.ObsObject;
|
||||
import com.obs.services.model.PutObjectResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
public interface ObsService {
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
R<ObsInfo> uploadFile(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 删除文件从OBS
|
||||
*
|
||||
* @param objectKey 文件在OBS中的键
|
||||
* @return 返回一个包含删除操作结果的响应对象。
|
||||
*/
|
||||
public R<DeleteObjectResult> deleteFile(String objectKey);
|
||||
|
||||
/**
|
||||
* 下载文件从OBS
|
||||
*
|
||||
* @param objectKey 文件在OBS中的键
|
||||
* @return 返回下载文件的结果
|
||||
*/
|
||||
public R<ObsObject> downloadFile(String objectKey);
|
||||
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param files 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
R<List<ObsInfo>> uploadFiles(MultipartFile[] files);
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
package com.bonus.obs.service.impl;
|
||||
|
||||
import com.alibaba.nacos.common.utils.UuidUtils;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.obs.domain.ObsInfo;
|
||||
import com.bonus.obs.service.ObsService;
|
||||
import com.bonus.obs.utils.ObsUtils;
|
||||
import com.obs.services.model.DeleteObjectResult;
|
||||
import com.obs.services.model.ObsObject;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@Service
|
||||
public class ObsServiceImpl implements ObsService {
|
||||
@Resource
|
||||
private ObsUtils obsUtils;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Override
|
||||
public R<ObsInfo> uploadFile(MultipartFile file) {
|
||||
try {
|
||||
String originalFilename = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空");
|
||||
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||
String objectKey = UuidUtils.generateUuid() + extension;
|
||||
objectKey = FileUtils.generateObjectName(objectKey);
|
||||
|
||||
ObsInfo obsInfo = obsUtils.uploadFile(objectKey, FileUtils.multipartFileToFile(file));
|
||||
return R.ok(obsInfo, "文件上传成功");
|
||||
} catch (Exception e) {
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件从OBS
|
||||
*
|
||||
* @param objectKey 文件在OBS中的键
|
||||
*/
|
||||
@Override
|
||||
public R<DeleteObjectResult> deleteFile(String objectKey) {
|
||||
return obsUtils.deleteFile(objectKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件从OBS
|
||||
*
|
||||
* @param objectKey 文件在OBS中的键
|
||||
*/
|
||||
@Override
|
||||
public R<ObsObject> downloadFile(String objectKey) {
|
||||
return obsUtils.downloadFile(objectKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param files 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Override
|
||||
public R<List<ObsInfo>> uploadFiles(MultipartFile[] files) {
|
||||
// 调整线程池大小
|
||||
int threadPoolSize = 10;
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
|
||||
try {
|
||||
List<Future<ObsInfo>> futures = new ArrayList<>();
|
||||
for (MultipartFile multipartFile : files) {
|
||||
futures.add(executorService.submit(() -> {
|
||||
String originalFilename = Objects.requireNonNull(multipartFile.getOriginalFilename(), "文件名不能为空");
|
||||
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||
String objectKey = UuidUtils.generateUuid() + extension;
|
||||
objectKey = FileUtils.generateObjectName(objectKey);
|
||||
return obsUtils.uploadFile(objectKey, FileUtils.multipartFileToFile(multipartFile));
|
||||
}));
|
||||
}
|
||||
List<ObsInfo> obsInfos = futures.stream().map(future -> {
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return R.ok(obsInfos, "文件上传成功");
|
||||
} catch (Exception e) {
|
||||
return R.fail("File upload failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 9205
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 5GB
|
||||
max-request-size: 5GB
|
||||
application:
|
||||
# 应用名称
|
||||
name: bonus-obs
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: dev
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 192.168.0.56:8848
|
||||
namespace: 9cde1ce1-98bc-4b9c-9213-f1fbf8a5b3cc
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 192.168.0.56:8848
|
||||
namespace: 9cde1ce1-98bc-4b9c-9213-f1fbf8a5b3cc
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/bonus-file" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.bonus" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
</configuration>
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-modules</artifactId>
|
||||
<version>24.8.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>bonus-oss</artifactId>
|
||||
<description>
|
||||
bonus-modules-oss存储服务
|
||||
</description>
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FastDFS -->
|
||||
<dependency>
|
||||
<groupId>com.github.tobato</groupId>
|
||||
<artifactId>fastdfs-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Common Swagger -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-swagger</artifactId>
|
||||
</dependency>
|
||||
<!--阿里云oss 存储-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.10.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.bonus.oss;
|
||||
|
||||
import com.bonus.common.swagger.annotation.EnableCustomSwagger2;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 。
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@EnableCustomSwagger2
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||
public class BonusOssApplication {
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(BonusOssApplication.class, args);
|
||||
System.out.println("(♥◠‿◠)ノ゙ oss存储服务模块启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||
" .-------. ____ __ \n" +
|
||||
" | _ _ \\ \\ \\ / / \n" +
|
||||
" | ( ' ) | \\ _. / ' \n" +
|
||||
" |(_ o _) / _( )_ .' \n" +
|
||||
" | (_,_).' __ ___(_ o _)' \n" +
|
||||
" | |\\ \\ | || |(_,_)' \n" +
|
||||
" | | \\ `' /| `-' / \n" +
|
||||
" | | \\ / \\ / \n" +
|
||||
" ''-' `'-' `-..-' ");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
package com.bonus.oss.controller;
|
||||
|
||||
import com.aliyun.oss.model.OSSObject;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.oss.domain.OssInfo;
|
||||
import com.bonus.oss.service.OssService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/oss")
|
||||
public class OssController {
|
||||
@Resource
|
||||
private OssService ossService;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@PostMapping("/uploadFile")
|
||||
public R<OssInfo> uploadFile(MultipartFile file) {
|
||||
return ossService.uploadFile(file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param files 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@PostMapping("/uploadFiles")
|
||||
public R<List<OssInfo>> uploadFiles(MultipartFile[] files) {
|
||||
return ossService.uploadFiles(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param objectKey oss文件存储地址
|
||||
*/
|
||||
@GetMapping("/downloadFile")
|
||||
public void downloadFile(HttpServletResponse response, @RequestParam String objectKey) throws UnsupportedEncodingException {
|
||||
R<OSSObject> ossObjectR = ossService.downloadFile(objectKey);
|
||||
if (ossObjectR.getData() == null) {
|
||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||
return;
|
||||
}
|
||||
|
||||
OSSObject ossObject = ossObjectR.getData();
|
||||
String safeFileName = FileUtils.getName(objectKey); // 假设这个方法进行了恰当的文件名清理和验证
|
||||
if (!StringUtils.hasText(safeFileName)) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
return;
|
||||
}
|
||||
|
||||
String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8.toString());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"");
|
||||
|
||||
try (InputStream inputStream = ossObject.getObjectContent();
|
||||
OutputStream outputStream = response.getOutputStream()) {
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
outputStream.flush();
|
||||
} catch (IOException e) {
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
} catch (IllegalArgumentException e) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
} catch (Exception e) {
|
||||
// 记录异常信息到日志
|
||||
log.error("文件下载过程中出现未知异常,原因是:", e);
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件删除
|
||||
*
|
||||
* @param objectKey 文件路径
|
||||
* @return 删除结果
|
||||
*/
|
||||
@PostMapping("/deleteFile")
|
||||
public R<T> deleteFile(@RequestParam String objectKey) {
|
||||
return ossService.deleteFile(objectKey);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package com.bonus.oss.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* OssInfo类用于封装OSS(Object Storage Service)对象的相关信息。
|
||||
* 通过此类,可以方便地管理和访问OSS对象的元数据,如名称、路径、大小等。
|
||||
*
|
||||
*
|
||||
* @author jiang
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
public class OssInfo {
|
||||
/**
|
||||
* 文件或对象的名称。
|
||||
* 用于唯一标识OSS中的一个对象。
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 文件在OSS中的存储路径。
|
||||
* 该路径不包含Bucket名称,仅指对象在Bucket内的相对路径。
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件或对象的大小。
|
||||
* 以字符串形式表示,单位可能为字节、KB、MB等。
|
||||
*/
|
||||
private String length;
|
||||
|
||||
/**
|
||||
* 文件的类型。
|
||||
* 可以是文件的MIME类型,或者根据文件扩展名推测的类型。
|
||||
*/
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 存储文件的Bucket名称。
|
||||
* Bucket是OSS中用于存储对象的容器,每个对象必须属于某个Bucket。
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package com.bonus.oss.service;
|
||||
|
||||
import com.aliyun.oss.model.OSSObject;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.oss.domain.OssInfo;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author jiang
|
||||
*/
|
||||
public interface OssService {
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param param 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
R<OssInfo> uploadFile(MultipartFile param);
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param objectKey oss文件存储地址
|
||||
* @return OSSObject对象
|
||||
*/
|
||||
R<OSSObject> downloadFile(String objectKey);
|
||||
|
||||
/**
|
||||
* 文件删除
|
||||
*
|
||||
* @param objectKey oss文件存储地址
|
||||
* @return 操作结果
|
||||
*/
|
||||
R<T> deleteFile(String objectKey);
|
||||
/**
|
||||
* 多文件上传
|
||||
*
|
||||
* @param files 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
R<List<OssInfo>> uploadFiles(MultipartFile[] files);
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
package com.bonus.oss.service.impl;
|
||||
|
||||
import com.alibaba.nacos.common.utils.UuidUtils;
|
||||
import com.aliyun.oss.model.OSSObject;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.utils.file.FileUtils;
|
||||
import com.bonus.oss.domain.OssInfo;
|
||||
import com.bonus.oss.service.OssService;
|
||||
import com.bonus.oss.utils.OssUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author jiang
|
||||
*/
|
||||
@Service
|
||||
public class OssServiceImpl implements OssService {
|
||||
|
||||
@Resource
|
||||
private OssUtils ossUtils;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Override
|
||||
public R<OssInfo> uploadFile(MultipartFile file) {
|
||||
if (ObjectUtils.isEmpty(file)) {
|
||||
return R.fail("File is null.");
|
||||
}
|
||||
try {
|
||||
String originalFilename = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空");
|
||||
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||
String objectKey = UuidUtils.generateUuid() + extension;
|
||||
objectKey = FileUtils.generateObjectName(objectKey);
|
||||
return ossUtils.upload(objectKey, FileUtils.multipartFileToFile(file));
|
||||
} catch (Exception e) {
|
||||
return R.fail("File upload failed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param objectKey oss文件存储地址
|
||||
*/
|
||||
@Override
|
||||
public R<OSSObject> downloadFile(String objectKey) {
|
||||
if (ObjectUtils.isEmpty(objectKey)) {
|
||||
return R.fail("objectKey is null.");
|
||||
}
|
||||
return ossUtils.download(objectKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件删除
|
||||
*
|
||||
* @param objectKey oss文件存储地址
|
||||
*/
|
||||
@Override
|
||||
public R<T> deleteFile(String objectKey) {
|
||||
try {
|
||||
if (ObjectUtils.isNotEmpty(objectKey)) {
|
||||
return ossUtils.delete(objectKey);
|
||||
} else {
|
||||
return R.fail("objectKey is null.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return R.fail("File delete failed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多文件上传
|
||||
*
|
||||
* @param files 文件流
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Override
|
||||
public R<List<OssInfo>> uploadFiles(MultipartFile[] files) {
|
||||
// 调整线程池大小
|
||||
int threadPoolSize = 10;
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
|
||||
try {
|
||||
List<Future<OssInfo>> futures = new ArrayList<>();
|
||||
for (MultipartFile multipartFile : files) {
|
||||
futures.add(executorService.submit(() -> {
|
||||
String originalFilename = Objects.requireNonNull(multipartFile.getOriginalFilename(), "文件名不能为空");
|
||||
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||
String objectKey = UuidUtils.generateUuid() + extension;
|
||||
objectKey = FileUtils.generateObjectName(objectKey);
|
||||
return ossUtils.upload(objectKey, FileUtils.multipartFileToFile(multipartFile)).getData();
|
||||
}));
|
||||
}
|
||||
List<OssInfo> obsInfos = futures.stream().map(future -> {
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return R.ok(obsInfos, "文件上传成功");
|
||||
} catch (Exception e) {
|
||||
return R.fail("File upload failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 9204
|
||||
# Spring
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 5GB
|
||||
max-request-size: 5GB
|
||||
application:
|
||||
# 应用名称
|
||||
name: bonus-oss
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: dev
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 192.168.0.56:8848
|
||||
namespace: 9cde1ce1-98bc-4b9c-9213-f1fbf8a5b3cc
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 192.168.0.56:8848
|
||||
namespace: 9cde1ce1-98bc-4b9c-9213-f1fbf8a5b3cc
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/bonus-file" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.bonus" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
</configuration>
|
||||
|
|
@ -6,7 +6,6 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import com.bonus.common.log.annotation.SysLog;
|
||||
import com.bonus.common.log.enums.OperaType;
|
||||
import com.bonus.system.api.domain.SysDictData;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.bonus.common.core.web.domain.AjaxResult;
|
|||
import com.bonus.system.api.domain.SysLogsVo;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@
|
|||
<module>bonus-gen</module>
|
||||
<module>bonus-job</module>
|
||||
<module>bonus-file</module>
|
||||
<module>bonus-oss</module>
|
||||
<module>bonus-obs</module>
|
||||
<module>bonus-mongodb</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>bonus-modules</artifactId>
|
||||
|
|
|
|||
Loading…
Reference in New Issue