Merge branch 'feature/rm_up_file' into 'master'

Feature/rm up file

See merge request shanghai/oss!1
This commit is contained in:
赵勇 2021-08-19 01:42:17 +00:00
commit b96234b0ad
60 changed files with 1891 additions and 12 deletions

View File

@ -120,7 +120,7 @@ CREATE TABLE `file` (
`is_delete` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否逻辑删除(0未删除1已删除)',
PRIMARY KEY (`id`),
INDEX idx_create_at(create_at),
INDEX idx_url_md5_app_code(url_md5, app_code),
UNIQUE KEY uk_file_uuid(`file_uuid`),
UNIQUE KEY uk_file_md5(`file_uuid`),
UNIQUE KEY idx_app_code_bucket_name_file_url(`app_channel_bucket_no`, `directory`, `file_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件上传记录';

View File

@ -0,0 +1,14 @@
INSERT INTO `app_channel_bucket`( `app_channel_bucket_no`, `app_code`, `channel_code`, `bucket_name`)
VALUES ( 'zeus:aliyun:zeus', 'zeus', 'aliyun', 'axzo-oss');
INSERT INTO `file_app`(`app_code`, `app_name`, `status`, `remark` )
VALUES ('zeus', 'zeus', 1, 'zeus');
INSERT INTO `file_business_scene`(`app_channel_bucket_no`, `app_code`, `channel_code`, `bucket_name`, `business_scene`, `directory`)
VALUES ('zeus:aliyun:zeus', 'zeus', 'aliyun', 'axzo-oss', 'recruit_collect', 'zeus/recruit_collect');
INSERT INTO `file_channel`(`channel_code`, `channel_name`, `status`, `priority`)
VALUES ('aliyun', '阿里云', 1, 1);
INSERT INTO `file_upload_config`(`app_channel_bucket_no`, `app_code`, `channel_code`, `bucket_name`, `directory`, `storage_unit`, `storage_size`, `file_format`)
VALUES ('zeus:aliyun:zeus', 'zeus', 'aliyun', 'axzo-oss', 'zeus/recruit_collect', 'MB', 5, 'xlsx');

View File

@ -106,6 +106,14 @@
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -15,6 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@MapperScan(basePackages = {"cn.axzo.oss.dal.mapper"})
@SpringBootApplication(scanBasePackages = {"cn.axzo.oss"})
@MapperScan(basePackages = {"cn.axzo.oss.dal.mapper"})
public class Bootstrap {
public static void main(String[] args) {

View File

@ -0,0 +1,35 @@
package cn.axzo.oss.client.annotation.resolver;
import cn.axzo.oss.common.annotation.ApiAccessCheck;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* @program: oss
* @description: 访问校验实现
* @author: mr.jie
* @date: 2021-07-28 01:23
**/
@Aspect
@Component
@Slf4j
public class ApiAccessCheckResolver {
@Around("@annotation(apiAccessCheck)")
public Object methodHandler(ProceedingJoinPoint jp, ApiAccessCheck apiAccessCheck)
throws Throwable {
Object[] params = jp.getArgs();
log.info("methodHandler params = {}", JSONObject.toJSON(params));
Object result = jp.proceed();
return result;
}
}

View File

@ -1,10 +1,15 @@
package cn.axzo.oss.client.controller;
import cn.axzo.oss.common.utils.BeanConvertUtil;
import cn.axzo.oss.http.api.ServerFileServiceApi;
import cn.axzo.oss.http.model.ServerFileDeleteRequest;
import cn.axzo.oss.http.model.ServerFileUploadRequest;
import cn.axzo.oss.http.model.ServerFileUploadResponse;
import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto;
import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto;
import cn.axzo.oss.service.api.FileService;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@ -20,13 +25,25 @@ import javax.validation.Valid;
@RestController
public class ServerFileController implements ServerFileServiceApi {
@Override
public CommonResponse<ServerFileUploadResponse> upload(@Valid @RequestBody ServerFileUploadRequest request) {
return null;
}
@Autowired
private FileService fileService;
@Override
public CommonResponse<ServerFileUploadResponse> upload(@Valid @RequestBody ServerFileUploadRequest request) {
ServerFileUploadDto dto = BeanConvertUtil.copyBean(request, ServerFileUploadDto.class);
request.setFileContent(null);
return CommonResponse.success(fileService.upload(dto));
}
/**
* 根据文件url删除文件
* @param request
* @return
*/
@Override
public CommonResponse delete(@Valid @RequestBody ServerFileDeleteRequest request) {
return null;
ServerFileDeleteDto dto = BeanConvertUtil.copyBean(request, ServerFileDeleteDto.class);
fileService.delete(dto);
return CommonResponse.success();
}
}

View File

@ -0,0 +1,74 @@
package cn.axzo.oss.client.handler;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.exception.BizException;
import cn.axzo.oss.common.exception.S3Exception;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @program: oss
* @description: 全局异常捕捉
* @author: mr.jie
* @date: 2021-07-29 19:51
**/
@Slf4j
@RestControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(value = BizException.class)
public CommonResponse bizException(BizException e){
log.warn("[oss] ControllerExceptionHandler.bizException Exception", e);
return CommonResponse.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(value = S3Exception.class)
public CommonResponse bizException(S3Exception e){
log.warn("[oss] ControllerExceptionHandler.s3Exception Exception", e);
return CommonResponse.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(value = IllegalArgumentException.class)
public CommonResponse handleIllegalArgumentException(IllegalArgumentException e) {
log.error("[oss] ControllerExceptionHandler.handleIllegalArgumentException Exception message ", e);
return CommonResponse.error(CodeEnum.INVALID_PARAMETER.getCode(), e.getMessage());
}
@ExceptionHandler(value = DuplicateKeyException.class)
public CommonResponse handleDuplicateKeyException(DuplicateKeyException e) {
log.error("[oss] ControllerExceptionHandler.handleDuplicateKeyException Exception message ", e);
return CommonResponse.error(CodeEnum.REPEATED_REQUEST.getCode(), e.getCause().getMessage());
}
@ExceptionHandler(value = Exception.class)
public CommonResponse handleOtherException(Exception e) {
log.error("[oss] ControllerExceptionHandler.handleOtherException Exception", e);
return CommonResponse.error(CodeEnum.SYSTEM_ERROR.getCode(), CodeEnum.SYSTEM_ERROR.getMessage());
}
@ExceptionHandler(value = Throwable.class)
public CommonResponse handleThrowableException(Throwable e) {
log.error("[oss] ControllerExceptionHandler.handleThrowableException Exception", e);
return CommonResponse.error(CodeEnum.SYSTEM_ERROR.getCode(), CodeEnum.SYSTEM_ERROR.getMessage());
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public CommonResponse handleArgumentValidException(MethodArgumentNotValidException e){
log.warn("[oss] ControllerExceptionHandler.handleArgumentValidException Exception", e.getMessage());
BindingResult bindingResult = e.getBindingResult();
StringBuilder errorMsg = new StringBuilder();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
if (-1 == errorMsg.indexOf(fieldError.getDefaultMessage())) {
errorMsg.append(fieldError.getDefaultMessage()).append(",");
}
}
errorMsg.deleteCharAt(errorMsg.length() - 1);
return CommonResponse.error(CodeEnum.INVALID_PARAMETER.getCode(), errorMsg.toString());
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>cn.axzo.oss</groupId>
<artifactId>oss</artifactId>
<version>1.0.0</version>
<version>${revision}</version>
</parent>
<artifactId>oss-common</artifactId>
@ -70,6 +70,10 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,18 @@
package cn.axzo.oss.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @program: oss
* @description: 访问校验
* @author: mr.jie
* @date: 2021-07-28 00:50
**/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiAccessCheck {
}

View File

@ -8,4 +8,56 @@ package cn.axzo.oss.common.constans;
* @since 2021-07-15 11:45
*/
public abstract class CommonConstants {
/**
* 在指定字符串未找字符串的位置
*/
public static final int NOT_FOUND_INDEX_OF = -1;
/**
* 1
*/
public static final int ONE = 1;
/**
* 是否删除
*/
public interface TableDelete {
/**
* 未删除
*/
Integer UN_DELETED = 0;
/**
* 已删除
*/
Integer DELETED = 1;
}
/**
* 文件状态
*/
public interface FileStatus {
/**
* 处理中
*/
Integer PROCESSING = 0;
/**
* 上传成功
*/
Integer SUCCESS = 1;
/**
* 上传失败
*/
Integer FAIL = 2;
/**
* 已删除
*/
Integer DELETED = 3;
}
}

View File

@ -0,0 +1,50 @@
package cn.axzo.oss.common.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 系统返回码枚举类
*
* @author zhaoyong_sh
* @see CodeEnum
* @since 2021-05-17 15:57
*/
@RequiredArgsConstructor
@Getter
public enum CodeEnum implements EnumBase<Integer> {
/**
* 系统返回码枚举类
*/
SUCCESS(200, "success"),
SYSTEM_ERROR(500, "system error"),
FAIL(400, "service exception"),
/**
* 0-100 系统相关 错误码
*/
INVALID_PARAMETER(1, "无效的参数"),
REPEATED_REQUEST(2, "重复的请求"),
/**
* 101-200 业务场景
*/
S3_CLIENT_NULL(101,"oss client is null"),
APP_CODE_NOT_FOUND(102,"app code not found"),
APP_CHANNEL_BUCKET_NOT_FOUND(103,"channel bucket not found"),
APP_CHANNEL_NOT_FOUND(104,"channel not found"),
NO_CHANNELS_AVAILABLE(105,"no channels available"),
NO_UPLOAD_CONFIG(106,"no upload config"),
FILE_SIZE_EXCEEDS_LIMIT(107,"file size exceeds limit"),
NOT_FILE_FORMAT(108,"failed to get file format"),
FILE_FORMAT_NOT_SUPPORTED(109,"file format is not supported"),
FILE_UPLOAD_FAILED(109,"file upload failed"),
;
private final Integer code;
private final String message;
}

View File

@ -0,0 +1,27 @@
package cn.axzo.oss.common.enums;
/**
* @author: zhangran
* @date: 20210805 21:31
* @description: 文件类符号
**/
public enum FileClassEnum {
SEPARATOR_CHAR("/", "默认 分割器"),
DOT(".", "文件命后缀 如abc.xlsx"),
COMMA(",", "逗号"),
;
FileClassEnum(String type, String desc) {
this.type = type;
this.desc = desc;
}
public final String type;
public final String desc;
}

View File

@ -0,0 +1,35 @@
package cn.axzo.oss.common.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author: zhangran
* @date: 20210802 14:48
* @description:
**/
@Getter
@RequiredArgsConstructor
public enum FileStatusEnum implements EnumBase<Integer> {
/**
* 处理中
*/
STATUS_PROCESSING(0, "处理中"),
/**
* 上传成功
*/
STATUS_UPLOAD_SUCCESS(1, "上传成功"),
/**
* 上传失败
*/
STATUS_UPLOAD_FAIL(2, "上传失败"),
/**
* 已删除
*/
STATUS_DELETE(3, "已删除"),
;
private final Integer code;
private final String message;
}

View File

@ -0,0 +1,43 @@
package cn.axzo.oss.common.enums;
import java.util.Arrays;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @ClassName: cn.axzo.oss.common.enums
* @description:
* @author:
* @date: 2021/8/119:04
**/
@Getter
@RequiredArgsConstructor
public enum StorageUnitEnum {
/**
* B
*/
B(1, "B"),
/**
* KB
*/
KB(B.size * 1024, "KB"),
/**
* MB
*/
MB(KB.size * 1024, "MB"),
/**
* GB
*/
GB(MB.size * 1024, "GB"),
;
private final Integer size;
private final String unit;
public static StorageUnitEnum value(String unit) {
return Arrays.stream(values()).filter(storageUnitEnum -> storageUnitEnum.getUnit().equals(unit))
.findFirst().orElse(null);
}
}

View File

@ -0,0 +1,97 @@
package cn.axzo.oss.common.exception;
import cn.axzo.oss.common.enums.CodeEnum;
import lombok.Getter;
import lombok.Setter;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* 业务异常
*
* @author zhaoyong_sh
* @see BizException
* @since 2021-05-17 15:56
*/
@Getter
@Setter
public class BizException extends RuntimeException {
private Integer code;
private String message;
/**
* 需返回的业务数据
*/
private Object data;
public BizException(Integer code) {
this.code = code;
}
public BizException(CodeEnum returnCode) {
super(returnCode.getMessage());
this.code = returnCode.getCode();
this.message = returnCode.getMessage();
}
public BizException(CodeEnum returnCode, String message) {
super(returnCode.getMessage());
this.code = returnCode.getCode();
this.message = message;
}
public BizException(Integer code, Throwable cause) {
super(null, cause);
this.code = code;
}
public BizException(Integer code, String message, Object data) {
super(message);
this.code = code;
this.message = message;
this.data = data;
}
public BizException(Integer code, String message, Object data, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
this.data = data;
}
public static void error(boolean b, CodeEnum returnCode) {
if (!b) {
throw new BizException(returnCode.getCode(), returnCode.getMessage(), null);
}
}
public static void error(boolean b, CodeEnum returnCode, String message) {
if (!b) {
throw new BizException(returnCode.getCode(), message, null);
}
}
public static void error(boolean b, Integer code, String message) {
if (!b) {
throw new BizException(code, message, null);
}
}
public static void error(boolean b, Integer code, String message, Object data) {
if (!b) {
throw new BizException(code, message, data);
}
}
public static void error(CodeEnum returnCode) {
throw new BizException(returnCode.getCode(), returnCode.getMessage(), null);
}
public static void isEmpty(@Nullable Object obj, CodeEnum returnCode) {
if (ObjectUtils.isEmpty(obj)) {
throw new BizException(returnCode.getCode(), returnCode.getMessage(), null);
}
}
}

View File

@ -0,0 +1,43 @@
package cn.axzo.oss.common.exception;
/**
* @program: oss
* @description: 文件上传异常
* @author: mr.jie
* @date: 2021-07-29 19:28
**/
public class S3Exception extends RuntimeException {
private Integer code;
private String msg;
public S3Exception(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public void setCode(Integer code) {
this.code = code;
}
public Integer getCode() {
return code;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
@Override
public String toString() {
return "S3Exception{" +
"code=" + code +
", msg='" + msg + '\'' +
'}';
}
}

View File

@ -0,0 +1,28 @@
package cn.axzo.oss.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:53
* @Version 0.0.1
**/
@Slf4j
public class BeanConvertUtil {
public static <T> T copyBean(Object source, Class<T> targetCls) {
T target = null;
if (Utility.objIsNotNull(source)) {
try {
target = BeanUtils.instantiateClass(targetCls);
BeanUtils.copyProperties(source, target);
} catch (BeansException e) {
log.warn("copyBean error, e = {}", e);
}
}
return target;
}
}

View File

@ -0,0 +1,21 @@
package cn.axzo.oss.common.utils;
import com.alibaba.fastjson.JSONObject;
/**
* @Author admin
* @Description
* @Date 2021/7/28 23:37
* @Version 0.0.1
**/
public class JsonUtil {
/**
* 对象转json字符串
* @param obj
* @return
*/
public static String obj2Str(Object obj){
return JSONObject.toJSONString(obj);
}
}

View File

@ -0,0 +1,128 @@
package cn.axzo.oss.common.utils;
import cn.axzo.oss.common.enums.FileClassEnum;
import cn.axzo.oss.common.enums.StorageUnitEnum;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:54
* @Version 0.0.1
**/
@Slf4j
public class Utility {
/**
* 判断对象为null
* @param obj
* @return
*/
public static boolean objIsNull(Object obj){
return Objects.isNull(obj);
}
/**
* 判断对象不为null
* @param obj
* @return
*/
public static boolean objIsNotNull(Object obj){
return !Objects.isNull(obj);
}
/**
* 字符串为空
* @return
*/
public static boolean isBlank(String str){
int strLen;
if (str == null || (strLen = str.length()) == 0){
return Boolean.TRUE;
}
for (int i = 0; i< strLen; i++){
if ((Character.isWhitespace(str.charAt(i)) == false)){
return false;
}
}
return true;
}
/**
* 获取md5摘要
* @param content
* @return
*/
public static String getMd5(String content){
if (isBlank(content)){
return content;
}
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(content.getBytes(StandardCharsets.UTF_8));
return Hex.encodeHexString(md5.digest());
} catch (NoSuchAlgorithmException e) {
log.warn("getMd5 fail = {}",e.getMessage());
}
return content;
}
/**
* 获取文件的MD5值
* @param bytes
* @return
*/
public static String getMd5(byte [] bytes) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(bytes);
return Hex.encodeHexString(md5.digest());
} catch (NoSuchAlgorithmException e) {
log.warn("getMd5 fail = {}",e.getMessage());
}
return "";
}
/**
* 容量换算
* @return
*/
public static int capacityConversion(int size, String storageUnit) {
StorageUnitEnum storageUnitEnum = StorageUnitEnum.valueOf(storageUnit);
if (objIsNull(storageUnitEnum)) {
return 0;
}
return storageUnitEnum.getSize() * size;
}
/**
* 生成UUID
* @return
*/
public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
/**
* 生成file key
* @return
*/
public static String generateFileKey(String directory, String fileUuid, String fileFormat){
StringBuilder tgtFileKey = new StringBuilder(directory);
tgtFileKey.append(FileClassEnum.SEPARATOR_CHAR.type);
tgtFileKey.append(fileUuid);
tgtFileKey.append(FileClassEnum.DOT.type);
tgtFileKey.append(fileFormat);
return tgtFileKey.toString();
}
}

View File

@ -2,6 +2,7 @@ package cn.axzo.oss.dal.repository;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
@ -13,4 +14,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface AppChannelBucketDao extends IService<AppChannelBucket> {
/**
* 通过appcode获取文件渠道桶信息
*
* @param appCode appcode
* @return
*/
List<AppChannelBucket> getByAppCode(String appCode);
}

View File

@ -13,4 +13,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface FileAppDao extends IService<FileApp> {
/**
* 通过appCode获取应用信息
* @param appCode
* @return
*/
FileApp getByAppCode(String appCode);
}

View File

@ -13,4 +13,12 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface FileBusinessSceneDao extends IService<FileBusinessScene> {
/**
* 指定appcode文件业务场景
*
* @param bucketNo 应用编码
* @param bizScene
* @return
*/
FileBusinessScene getByBucketNoAndScene(String bucketNo, String bizScene);
}

View File

@ -2,6 +2,7 @@ package cn.axzo.oss.dal.repository;
import cn.axzo.oss.dal.entity.FileChannel;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
@ -13,4 +14,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface FileChannelDao extends IService<FileChannel> {
/**
* 通过channelCode获取最高优化级可用文件渠道
* @param channelCodes
* @return
*/
List<FileChannel> getByChannelCode(List<String> channelCodes);
}

View File

@ -13,4 +13,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface FileDao extends IService<File> {
/**
* 根据url md5获取文件信息
* @param appCode
* @param urlMd5
* @return
*/
File getByAppCodeAndUrlMd5(String appCode, String urlMd5);
}

View File

@ -13,4 +13,12 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface FileUploadConfigDao extends IService<FileUploadConfig> {
/**
* 通过渠道码和桶名称获取获取指定上传配置
*
* @param bucketNo
* @param directory
* @return
*/
FileUploadConfig getByUploadConfig(String bucketNo, String directory);
}

View File

@ -1,9 +1,11 @@
package cn.axzo.oss.dal.repository.impl;
import cn.axzo.oss.common.enums.IsDeleteEnum;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import cn.axzo.oss.dal.mapper.AppChannelBucketMapper;
import cn.axzo.oss.dal.repository.AppChannelBucketDao;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import org.springframework.stereotype.Repository;
/**
@ -15,6 +17,18 @@ import org.springframework.stereotype.Repository;
* @since 2021-07-23
*/
@Repository("appChannelBucketDao")
public class AppChannelBucketDaoImpl extends ServiceImpl<AppChannelBucketMapper, AppChannelBucket> implements AppChannelBucketDao {
public class AppChannelBucketDaoImpl extends
ServiceImpl<AppChannelBucketMapper, AppChannelBucket> implements AppChannelBucketDao {
/**
* 通过appcode获取文件渠道桶信息
*
* @param appCode appcode
* @return
*/
@Override
public List<AppChannelBucket> getByAppCode(String appCode) {
return lambdaQuery().eq(AppChannelBucket::getAppCode, appCode)
.eq(AppChannelBucket::getIsDelete, IsDeleteEnum.NO.getCode()).list();
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.oss.dal.repository.impl;
import cn.axzo.oss.common.enums.IsDeleteEnum;
import cn.axzo.oss.dal.entity.FileApp;
import cn.axzo.oss.dal.mapper.FileAppMapper;
import cn.axzo.oss.dal.repository.FileAppDao;
@ -17,4 +18,17 @@ import org.springframework.stereotype.Repository;
@Repository("fileAppDao")
public class FileAppDaoImpl extends ServiceImpl<FileAppMapper, FileApp> implements FileAppDao {
/**
* 通过appCode获取应用信息
*
* @param appCode
* @return
*/
@Override
public FileApp getByAppCode(String appCode) {
return lambdaQuery().eq(FileApp::getAppCode, appCode)
.eq(FileApp::getStatus, 1)
.eq(FileApp::getIsDelete, IsDeleteEnum.NO.getCode()).one();
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.oss.dal.repository.impl;
import cn.axzo.oss.common.enums.IsDeleteEnum;
import cn.axzo.oss.dal.entity.FileBusinessScene;
import cn.axzo.oss.dal.mapper.FileBusinessSceneMapper;
import cn.axzo.oss.dal.repository.FileBusinessSceneDao;
@ -15,6 +16,19 @@ import org.springframework.stereotype.Repository;
* @since 2021-07-23
*/
@Repository("fileBusinessSceneDao")
public class FileBusinessSceneDaoImpl extends ServiceImpl<FileBusinessSceneMapper, FileBusinessScene> implements FileBusinessSceneDao {
public class FileBusinessSceneDaoImpl extends
ServiceImpl<FileBusinessSceneMapper, FileBusinessScene> implements FileBusinessSceneDao {
/**
* 指定appcode文件业务场景
*
* @param bucketNo
* @return
*/
@Override
public FileBusinessScene getByBucketNoAndScene(String bucketNo, String bizScen) {
return lambdaQuery().eq(FileBusinessScene::getAppChannelBucketNo, bucketNo)
.eq(FileBusinessScene::getBusinessScene,bizScen)
.eq(FileBusinessScene::getIsDelete, IsDeleteEnum.NO.getCode()).one();
}
}

View File

@ -4,6 +4,7 @@ import cn.axzo.oss.dal.entity.FileChannel;
import cn.axzo.oss.dal.mapper.FileChannelMapper;
import cn.axzo.oss.dal.repository.FileChannelDao;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import org.springframework.stereotype.Repository;
/**
@ -17,4 +18,16 @@ import org.springframework.stereotype.Repository;
@Repository("fileChannelDao")
public class FileChannelDaoImpl extends ServiceImpl<FileChannelMapper, FileChannel> implements FileChannelDao {
/**
* 通过channelCode获取多个可用文件渠道
*
* @param channelCodes
* @return
*/
@Override
public List<FileChannel> getByChannelCode(List<String> channelCodes) {
return lambdaQuery().in(FileChannel::getChannelCode,channelCodes)
.eq(FileChannel::getStatus,1)
.orderByAsc(FileChannel::getPriority).list();
}
}

View File

@ -1,5 +1,7 @@
package cn.axzo.oss.dal.repository.impl;
import cn.axzo.oss.common.constans.CommonConstants.FileStatus;
import cn.axzo.oss.common.constans.CommonConstants.TableDelete;
import cn.axzo.oss.dal.entity.File;
import cn.axzo.oss.dal.mapper.FileMapper;
import cn.axzo.oss.dal.repository.FileDao;
@ -17,4 +19,10 @@ import org.springframework.stereotype.Repository;
@Repository("fileDao")
public class FileDaoImpl extends ServiceImpl<FileMapper, File> implements FileDao {
@Override
public File getByAppCodeAndUrlMd5(String appCode, String urlMd5) {
return lambdaQuery().eq(File::getAppCode, appCode).eq(File::getUrlMd5, urlMd5)
.eq(File::getStatus, FileStatus.SUCCESS).eq(File::getIsDelete, TableDelete.UN_DELETED)
.last("limit 1").one();
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.oss.dal.repository.impl;
import cn.axzo.oss.common.enums.IsDeleteEnum;
import cn.axzo.oss.dal.entity.FileUploadConfig;
import cn.axzo.oss.dal.mapper.FileUploadConfigMapper;
import cn.axzo.oss.dal.repository.FileUploadConfigDao;
@ -15,6 +16,20 @@ import org.springframework.stereotype.Repository;
* @since 2021-07-23
*/
@Repository("fileUploadConfigDao")
public class FileUploadConfigDaoImpl extends ServiceImpl<FileUploadConfigMapper, FileUploadConfig> implements FileUploadConfigDao {
public class FileUploadConfigDaoImpl extends
ServiceImpl<FileUploadConfigMapper, FileUploadConfig> implements FileUploadConfigDao {
/**
* 通过渠道码和桶名称获取获取指定上传配置
*
* @param bucketNo
* @param directory
* @return
*/
@Override
public FileUploadConfig getByUploadConfig(String bucketNo, String directory) {
return lambdaQuery().eq(FileUploadConfig::getAppChannelBucketNo, bucketNo)
.eq(FileUploadConfig::getDirectory, directory)
.eq(FileUploadConfig::getIsDelete, IsDeleteEnum.NO.getCode()).one();
}
}

View File

@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
*/
@FeignClient(
name = "oss",
url = "http://oss"
url = "http://oss:9123"
)
@RequestMapping(value = "/api/v1/server")
public interface ServerFileServiceApi {

View File

@ -14,7 +14,13 @@ import javax.validation.constraints.NotBlank;
@Data
public class ServerFileDeleteRequest {
@NotBlank(message = "appCode must not be null")
private String appCode;
@NotBlank(message = "url must not be null")
private String url;
@NotBlank(message = "operator must not be null")
private String operator;
}

View File

@ -19,7 +19,11 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -0,0 +1,13 @@
package cn.axzo.oss.integration.s3;
import cn.axzo.oss.integration.s3.base.BaseS3Service;
/**
* @program: oss
* @description: 阿里云oss服务
* @author: mr.jie
* @date: 2021-07-29 15:44
**/
public interface AliOssService extends BaseS3Service {
}

View File

@ -0,0 +1,55 @@
package cn.axzo.oss.integration.s3.base;
import java.io.FilterInputStream;
import java.io.InputStream;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
/**
* @Author admin
* @Description
* @Date 2021/7/28 23:46
* @Version 0.0.1
**/
public interface BaseS3Service {
int RECORD_LIMIT = 100;
// ttl 15m; 60 * 15 = 900s
int DEFAULT_TTL_S = 900;
/**
* upload file
*/
String uploadByStream(String bucketName, String tgtFileKey, InputStream srcStream);
/**
* upload multi file
*/
boolean uploadMultiFile(MultipartFile[] multipartFiles);
/**
* delete file
*/
boolean delFile(String bucket, String tgtFileKey);
/**
* download file
*/
ResponseEntity<byte[]> download(String tgtFileKey);
/**
* get file address
*
* @param ttl second
*/
String getFileAddress(String tgtFileKey, int ttl);
/**
* get S3Object stream
*/
FilterInputStream getS3ObjectStream(String tgtFileKey);
}

View File

@ -0,0 +1,17 @@
package cn.axzo.oss.integration.s3.base;
/**
* @program: oss
* @description: 客户端接口
* @author: mr.jie
* @date: 2021-07-29 18:07
**/
public interface OssClientBase {
Object getClient();
void initClient();
String getEndpoint();
}

View File

@ -0,0 +1,55 @@
package cn.axzo.oss.integration.s3.client;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.exception.S3Exception;
import cn.axzo.oss.common.utils.Utility;
import cn.axzo.oss.integration.s3.base.OssClientBase;
import cn.axzo.oss.integration.s3.config.AliOssConfig;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @program: oss
* @description: 阿里云服务客户端
* @author: mr.jie
* @date: 2021-07-29 15:49
**/
@Component
@Slf4j
public class AliOssClient implements OssClientBase {
private volatile static OSS instance = null;
@Autowired
private AliOssConfig aliOssConfig;
public OSS getClient(){
if (Utility.objIsNull(instance)){
throw new S3Exception(CodeEnum.S3_CLIENT_NULL.getCode(),"oss客户端未初始化");
}
return instance;
}
@PostConstruct
@Override
public void initClient(){
log.info("initClient endpoint = {}, accessKeyId = {},secretAccessKey = {}", aliOssConfig.getEndpoint(),aliOssConfig.getAccessKeyId(),aliOssConfig.getSecretAccessKey());
try {
instance = new OSSClientBuilder().build(aliOssConfig.getEndpoint(),aliOssConfig.getAccessKeyId(),aliOssConfig.getSecretAccessKey());
} catch (Exception e) {
instance = null;
log.error("initClient error = {}", e);
}
}
@Override
public String getEndpoint(){
return aliOssConfig.getEndpoint();
}
}

View File

@ -0,0 +1,35 @@
package cn.axzo.oss.integration.s3.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @program: oss
* @description: 阿里云配置
* @author: mr.jie
* @date: 2021-07-29 17:57
**/
@Component
public class AliOssConfig {
@Value("${aliyun.oss.accessKeyId}")
private String ACCESS_KEY_ID;
@Value("${aliyun.oss.secretAccessKey}")
private String SECRET_ACCESS_KEY;
@Value("${aliyun.oss.endpoint}")
private String ENDPOINT;
public String getAccessKeyId(){
return this.ACCESS_KEY_ID;
}
public String getSecretAccessKey(){
return this.SECRET_ACCESS_KEY;
}
public String getEndpoint(){
return this.ENDPOINT;
}
}

View File

@ -0,0 +1,103 @@
package cn.axzo.oss.integration.s3.impl;
import cn.axzo.oss.integration.s3.AliOssService;
import cn.axzo.oss.integration.s3.client.AliOssClient;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import java.io.FilterInputStream;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
* @program: oss
* @description: 阿里oss服务
* @author: mr.jie
* @date: 2021-07-29 15:45
**/
@Service
@Slf4j
public class AliOssServiceImpl implements AliOssService {
@Autowired
private AliOssClient aliOssClient;
/**
* upload file
*/
@Override
public String uploadByStream(String bucketName, String tgtFileKey, InputStream srcStream) {
OSS client = aliOssClient.getClient();
try {
client.putObject(bucketName, tgtFileKey, srcStream);
} catch (OSSException e) {
log.error("uploadByStream OSSException", e);
return "";
} catch (ClientException e) {
log.error("uploadByStream ClientException", e);
return "";
}
StringBuilder allBuilder = new StringBuilder();
allBuilder.append("https://");
allBuilder.append(bucketName);
allBuilder.append(".");
allBuilder.append(aliOssClient.getEndpoint());
allBuilder.append(tgtFileKey);
return allBuilder.toString();
}
/**
* upload multi file
*/
@Override
public boolean uploadMultiFile(MultipartFile[] multipartFiles) {
return false;
}
/**
* delete file
*/
@Override
public boolean delFile(String bucket, String tgtFileKey) {
log.debug("ali oss delFile bucket = {}, tatFileKey = {}", bucket, tgtFileKey);
try {
aliOssClient.getClient().deleteObject(bucket, tgtFileKey);
return Boolean.TRUE;
} catch (Exception e) {
log.error("ali oss delFile error = {}", e);
}
return Boolean.FALSE;
}
/**
* download file
*/
@Override
public ResponseEntity<byte[]> download(String tgtFileKey) {
return null;
}
/**
* get file address
*
* @param ttl second
*/
@Override
public String getFileAddress(String tgtFileKey, int ttl) {
return null;
}
/**
* get S3Object stream
*/
@Override
public FilterInputStream getS3ObjectStream(String tgtFileKey) {
return null;
}
}

View File

@ -0,0 +1,20 @@
package cn.axzo.oss.manager.api;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import java.util.List;
/**
* @author: zhangran
* @date: 20210803 15:30
* @description:
**/
public interface AppChannelBucketManager {
/**
* 通过appcode获取文件渠道桶信息
*
* @param appCode appcode
* @return
*/
AppChannelBucket getByAppCode(String appCode);
}

View File

@ -0,0 +1,21 @@
package cn.axzo.oss.manager.api;
import cn.axzo.oss.dal.entity.FileApp;
/**
* @author: zhangran
* @date: 20210803 15:28
* @description:
**/
public interface FileAppManager {
/**
* 通过appCode获取应用信息
*
* @param appCode
* @return
*/
FileApp getByAppCode(String appCode);
}

View File

@ -0,0 +1,20 @@
package cn.axzo.oss.manager.api;
import cn.axzo.oss.dal.entity.FileBusinessScene;
/**
* @author: zhangran
* @date: 20210803 15:42
* @description:
**/
public interface FileBusinessSceneManager {
/**
* 指定appcode文件业务场景
*
* @param bucketNo 应用编码
* @param bizScene
* @return
*/
FileBusinessScene getByBucketNoAndScene(String bucketNo,String bizScene);
}

View File

@ -0,0 +1,29 @@
package cn.axzo.oss.manager.api;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import cn.axzo.oss.dal.entity.FileChannel;
import java.util.List;
/**
* @author: zhangran
* @date: 20210803 15:33
* @description:
**/
public interface FileChannelManager {
/**
* 返回所有渠道 按优先级升序返回
*
* @param channelCodes
* @return
*/
List<FileChannel> listChannelCodeOrderByPriorityAsc(List<String> channelCodes);
/**
* 返回一个最优的渠道 升序第一个
*
* @param appChannelBuckets
* @return
*/
FileChannel getOptimalChannel(List<AppChannelBucket> appChannelBuckets );
}

View File

@ -0,0 +1,29 @@
package cn.axzo.oss.manager.api;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:48
* @Version 0.0.1
**/
public interface FileManager {
/**
* 删除文件
* @param bucketName 桶名称
* @param tgtFileKey 目标文件key
* @return
*/
boolean delete(String bucketName, String tgtFileKey);
/**
* 上传
*
* @param bulkName
* @param keyPath
* @param fileContent
* @return
*/
String uploadByStream(String bulkName, String keyPath, byte[] fileContent);
}

View File

@ -0,0 +1,20 @@
package cn.axzo.oss.manager.api;
import cn.axzo.oss.dal.entity.FileUploadConfig;
/**
* @author: zhangran
* @date: 20210803 15:43
* @description:
**/
public interface FileUploadConfigManager {
/**
* 通过渠道码和桶名称获取获取指定上传配置
*
* @param bucketNo
* @param directory
* @return
*/
FileUploadConfig getByUploadConfig(String bucketNo, String directory);
}

View File

@ -0,0 +1,26 @@
package cn.axzo.oss.manager.api.dto.request;
import lombok.Data;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:50
* @Version 0.0.1
**/
@Data
public class ServerFileDeleteDto {
/**
* 应用码
*/
private String appCode;
/**
* 文件地址
*/
private String url;
/**
* 操作人
*/
private String operator;
}

View File

@ -0,0 +1,19 @@
package cn.axzo.oss.manager.api.dto.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
* @program: oss
* @description: 文件上传
* @author: mr.jie
* @date: 2021-07-29 18:31
**/
@Data
public class ServerFileUploadDto {
private String appCode;
private String bizScene;
private String fileName;
private byte[] fileContent;
}

View File

@ -0,0 +1,11 @@
package cn.axzo.oss.manager.api.dto.response;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:51
* @Version 0.0.1
**/
public class ServerFileDeleteResponse {
}

View File

@ -0,0 +1,28 @@
package cn.axzo.oss.manager.api.dto.response;
import lombok.Data;
/**
* 服务端文件上传响应类
*
* @author zhaoyong
* @see ServerFileUploadResponse
* @since 2021-07-23 10:37
*/
@Data
public class ServerFileUploadResponse {
/**
* 文件 URL
*/
private String url;
/**
* 文件 Key
*/
private String fileKey;
/**
* URL MD5
*/
private String urlMd5;
}

View File

@ -0,0 +1,51 @@
package cn.axzo.oss.manager.impl;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.exception.BizException;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import cn.axzo.oss.dal.entity.FileChannel;
import cn.axzo.oss.dal.repository.AppChannelBucketDao;
import cn.axzo.oss.manager.api.AppChannelBucketManager;
import cn.axzo.oss.manager.api.FileChannelManager;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: zhangran
* @date: 20210803 15:44
* @description:
**/
@Service
@Slf4j
public class AppChannelBucketManagerImpl implements AppChannelBucketManager {
@Autowired
private AppChannelBucketDao appChannelBucketDao;
@Autowired
private FileChannelManager fileChannelManager;
/**
* 通过appcode获取文件渠道桶信息
*
* @param appCode appcode
* @return
*/
@Override
public AppChannelBucket getByAppCode(String appCode) {
List<AppChannelBucket> appChannelBuckets = appChannelBucketDao.getByAppCode(appCode);
BizException.isEmpty(appChannelBuckets, CodeEnum.APP_CHANNEL_BUCKET_NOT_FOUND);
// 找到最优渠道
FileChannel fileChannel = fileChannelManager.getOptimalChannel(appChannelBuckets);
AppChannelBucket appChannelBucket = appChannelBuckets.stream().filter(bucket ->
bucket.getChannelCode().equals(fileChannel.getChannelCode()))
.findFirst().orElse(null);
BizException.isEmpty(appChannelBucket, CodeEnum.APP_CHANNEL_NOT_FOUND);
return appChannelBucket;
}
}

View File

@ -0,0 +1,38 @@
package cn.axzo.oss.manager.impl;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.exception.BizException;
import cn.axzo.oss.dal.entity.FileBusinessScene;
import cn.axzo.oss.dal.repository.FileBusinessSceneDao;
import cn.axzo.oss.manager.api.FileBusinessSceneManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: zhangran
* @date: 20210803 16:36
* @description:
**/
@Service
@Slf4j
public class FileBusinessSceneManagerImpl implements FileBusinessSceneManager {
@Autowired
private FileBusinessSceneDao fileBusinessSceneDao;
/**
* 指定appcode文件业务场景
*
* @param bucketNo 应用编码
* @param bizScene
* @return
*/
@Override
public FileBusinessScene getByBucketNoAndScene(String bucketNo, String bizScene) {
FileBusinessScene fileBusinessScene = fileBusinessSceneDao
.getByBucketNoAndScene(bucketNo, bizScene);
BizException.isEmpty(fileBusinessScene, CodeEnum.APP_CHANNEL_NOT_FOUND);
return fileBusinessScene;
}
}

View File

@ -0,0 +1,56 @@
package cn.axzo.oss.manager.impl;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.exception.BizException;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import cn.axzo.oss.dal.entity.FileChannel;
import cn.axzo.oss.dal.repository.FileChannelDao;
import cn.axzo.oss.manager.api.FileChannelManager;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: zhangran
* @date: 20210803 16:28
* @description:
**/
@Service
@Slf4j
public class FileChannelManagerImpl implements FileChannelManager {
@Autowired
private FileChannelDao fileChannelDao;
/**
* 返回所有渠道 按优先级升序返回
*
* @param channelCodes
* @return
*/
@Override
public List<FileChannel> listChannelCodeOrderByPriorityAsc(List<String> channelCodes) {
List<FileChannel> fileChannels = fileChannelDao.getByChannelCode(channelCodes);
BizException.isEmpty(fileChannels, CodeEnum.APP_CHANNEL_NOT_FOUND);
return fileChannels;
}
/**
* 返回一个最优的渠道 升序第一个
*
* @param appChannelBuckets
* @return
*/
@Override
public FileChannel getOptimalChannel(List<AppChannelBucket> appChannelBuckets ) {
ArrayList<String> channels = Lists.newArrayList();
appChannelBuckets.forEach(appChannelBucket -> channels.add(appChannelBucket.getChannelCode()));
List<FileChannel> fileChannels = fileChannelDao.getByChannelCode(channels);
BizException.isEmpty(fileChannels, CodeEnum.APP_CHANNEL_NOT_FOUND);
return fileChannels.get(0);
}
}

View File

@ -0,0 +1,46 @@
package cn.axzo.oss.manager.impl;
import cn.axzo.oss.integration.s3.AliOssService;
import cn.axzo.oss.manager.api.FileManager;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:48
* @Version 0.0.1
**/
@Service
@Slf4j
public class FileManagerImpl implements FileManager {
@Autowired
private AliOssService aliOssService;
/**
* 删除文件
* @param bucketName 桶名称
* @param tgtFileKey 目标文件key
* @return
*/
@Override
public boolean delete(String bucketName, String tgtFileKey) {
return aliOssService.delFile(bucketName, tgtFileKey);
}
/**
* @param bulkName 默认的取值为
* @param keyPath key的前置路径
*/
public String uploadByStream(String bulkName, String keyPath, byte[] fileContent) {
InputStream inputStream = new ByteArrayInputStream(fileContent);
return aliOssService.uploadByStream(bulkName, keyPath, inputStream);
}
}

View File

@ -0,0 +1,37 @@
package cn.axzo.oss.manager.impl;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.exception.BizException;
import cn.axzo.oss.dal.entity.FileUploadConfig;
import cn.axzo.oss.dal.repository.FileUploadConfigDao;
import cn.axzo.oss.manager.api.FileUploadConfigManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: zhangran
* @date: 20210803 16:39
* @description:
**/
@Service
@Slf4j
public class FileUploadConfigManagerImpl implements FileUploadConfigManager {
@Autowired
private FileUploadConfigDao fileUploadConfigDao;
/**
* 通过渠道码和桶名称获取获取指定上传配置
*
* @param bucketNo
* @param directory
* @return
*/
@Override
public FileUploadConfig getByUploadConfig(String bucketNo, String directory) {
FileUploadConfig uploadConfig = fileUploadConfigDao.getByUploadConfig(bucketNo, directory);
BizException.isEmpty(uploadConfig, CodeEnum.NO_UPLOAD_CONFIG);
return uploadConfig;
}
}

View File

@ -0,0 +1,28 @@
package cn.axzo.oss.service.api;
import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto;
import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto;
import cn.axzo.oss.manager.api.dto.response.ServerFileUploadResponse;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:48
* @Version 0.0.1
**/
public interface FileService {
/**
* 删除文件
*
* @param dto
*/
void delete(ServerFileDeleteDto dto);
/**
* 上传
*
* @param request
*/
ServerFileUploadResponse upload(ServerFileUploadDto request);
}

View File

@ -0,0 +1,204 @@
package cn.axzo.oss.service.impl;
import cn.axzo.oss.common.constans.CommonConstants;
import cn.axzo.oss.common.constans.CommonConstants.FileStatus;
import cn.axzo.oss.common.constans.CommonConstants.TableDelete;
import cn.axzo.oss.common.enums.CodeEnum;
import cn.axzo.oss.common.enums.FileClassEnum;
import cn.axzo.oss.common.enums.FileStatusEnum;
import cn.axzo.oss.common.exception.BizException;
import cn.axzo.oss.common.utils.JsonUtil;
import cn.axzo.oss.common.utils.Utility;
import cn.axzo.oss.dal.entity.AppChannelBucket;
import cn.axzo.oss.dal.entity.File;
import cn.axzo.oss.dal.entity.FileApp;
import cn.axzo.oss.dal.entity.FileBusinessScene;
import cn.axzo.oss.dal.entity.FileUploadConfig;
import cn.axzo.oss.dal.repository.FileAppDao;
import cn.axzo.oss.dal.repository.FileDao;
import cn.axzo.oss.manager.api.AppChannelBucketManager;
import cn.axzo.oss.manager.api.FileBusinessSceneManager;
import cn.axzo.oss.manager.api.FileManager;
import cn.axzo.oss.manager.api.FileUploadConfigManager;
import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto;
import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto;
import cn.axzo.oss.manager.api.dto.response.ServerFileUploadResponse;
import cn.axzo.oss.service.api.FileService;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author admin
* @Description
* @Date 2021/7/28 22:48
* @Version 0.0.1
**/
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Autowired
private FileManager fileManager;
@Autowired
private FileDao fileDao;
@Autowired
private FileAppDao fileAppDao;
@Autowired
private AppChannelBucketManager appChannelBucketManager;
@Autowired
private FileBusinessSceneManager fileBusinessSceneManager;
@Autowired
private FileUploadConfigManager fileUploadConfigManager;
/**
* 删除文件
*
* @param dto
*/
@Override
public void delete(ServerFileDeleteDto dto) {
log.info("delete dto = {}", JsonUtil.obj2Str(dto));
// 检查app code
checkAppCode(dto.getAppCode());
String urlMd5 = Utility.getMd5(dto.getUrl());
log.info("delete urlMd5 = {}", urlMd5);
File file = fileDao.getByAppCodeAndUrlMd5(dto.getAppCode(), urlMd5);
if (Utility.objIsNull(file)) {
log.warn("delete file is null,url = {}, urlMd5 = {}", dto.getUrl(), urlMd5);
return;
}
String tgtFileKey = Utility
.generateFileKey(file.getDirectory(), file.getFileUuid(), file.getFileFormat());
log.debug("delete tgtFileKey = {}", tgtFileKey);
boolean deleteFlag = fileManager.delete(file.getBucketName(), tgtFileKey);
log.info("delete deleteFlag = {}", deleteFlag);
if (deleteFlag) {
File updateFile = new File();
updateFile.setIsDelete(TableDelete.DELETED);
updateFile.setStatus(FileStatus.DELETED);
updateFile.setUpdateBy(dto.getOperator());
updateFile.setId(file.getId());
fileDao.updateById(updateFile);
}
}
/**
* 检查appCode是否有效
*
* @param appCode
*/
private void checkAppCode(final String appCode) {
log.info("checkAppCode appCode = {}", appCode);
FileApp fileApp = fileAppDao.getByAppCode(appCode);
BizException.error(Utility.objIsNotNull(fileApp), CodeEnum.APP_CODE_NOT_FOUND);
}
/**
* 上传
*
* @param dto
*/
@Override
public ServerFileUploadResponse upload(ServerFileUploadDto dto) {
log.info("update fileName:{},appCode:{},bizScene:{}",
dto.getFileName(), dto.getAppCode(), dto.getBizScene());
// 检查appCode
checkAppCode(dto.getAppCode());
// 通过appcode获取文件渠道桶信息
AppChannelBucket appChannelBucket = appChannelBucketManager.getByAppCode(dto.getAppCode());
// 通过渠道桶编码获取到具体文件业务场景
FileBusinessScene scene = fileBusinessSceneManager
.getByBucketNoAndScene(appChannelBucket.getAppChannelBucketNo(), dto.getBizScene());
// 通过渠道码和桶名称获取获取指定上传配置
FileUploadConfig fileUploadConfig = fileUploadConfigManager
.getByUploadConfig(scene.getAppChannelBucketNo(), scene.getDirectory());
// 上传文件并生成file对象
File ossFile = generateFile(fileUploadConfig, dto);
return setResponse(ossFile);
}
/**
* 判断文件是否符合要求
*/
private String isFileConform(FileUploadConfig fileUploadConfig, int fileLength, String fileName) {
// 文件大小超出上限
int size = Utility
.capacityConversion(fileUploadConfig.getStorageSize(), fileUploadConfig.getStorageUnit());
BizException.error(size > fileLength, CodeEnum.FILE_SIZE_EXCEEDS_LIMIT);
// 文件格式判断
String[] formats = fileUploadConfig.getFileFormat().split(FileClassEnum.COMMA.type);
int lastIndexOf = fileName.lastIndexOf(FileClassEnum.DOT.type);
BizException
.error(lastIndexOf != CommonConstants.NOT_FOUND_INDEX_OF, CodeEnum.NOT_FILE_FORMAT);
// 是否包含指定格式
String fileFormat = fileName.substring(lastIndexOf + CommonConstants.ONE).toLowerCase();
boolean contains = Arrays.asList(formats).contains(fileFormat);
BizException.error(contains, CodeEnum.FILE_FORMAT_NOT_SUPPORTED);
return fileFormat;
}
private File generateFile(FileUploadConfig fileUploadConfig, ServerFileUploadDto dto) {
// 判断容量
String fileConform = isFileConform(fileUploadConfig, dto.getFileContent().length,
dto.getFileName());
String uuid = Utility.getUUID();
// 生成上传文件的唯一key
String tgtFileKey = Utility.generateFileKey(fileUploadConfig.getDirectory(), uuid, fileConform);
File ossFile = new File();
ossFile.setAppChannelBucketNo(fileUploadConfig.getAppChannelBucketNo());
ossFile.setAppCode(fileUploadConfig.getAppCode());
ossFile.setChannelCode(fileUploadConfig.getChannelCode());
ossFile.setBucketName(fileUploadConfig.getBucketName());
ossFile.setDirectory(fileUploadConfig.getDirectory());
ossFile.setStatus(FileStatusEnum.STATUS_UPLOAD_FAIL.getCode());
ossFile.setStorageUnit(fileUploadConfig.getStorageUnit());
ossFile.setStorageSize(fileUploadConfig.getStorageSize());
ossFile.setFileFormat(fileConform);
// 上传文件
String fileUrl = fileManager.uploadByStream(
fileUploadConfig.getBucketName(), tgtFileKey, dto.getFileContent());
// 保存失败
if (Utility.isBlank(fileUrl)) {
fileDao.save(ossFile);
throw new BizException(CodeEnum.FILE_UPLOAD_FAILED);
}
ossFile.setFileUuid(uuid);
ossFile.setFileUrl(fileUrl);
ossFile.setUrlMd5(Utility.getMd5(fileUrl));
ossFile.setStatus(FileStatusEnum.STATUS_UPLOAD_SUCCESS.getCode());
ossFile.setFileName(dto.getFileName());
ossFile.setFileMd5(Utility.getMd5(dto.getFileContent()));
fileDao.save(ossFile);
return ossFile;
}
private ServerFileUploadResponse setResponse(File ossFile){
ServerFileUploadResponse response = new ServerFileUploadResponse();
response.setUrl(ossFile.getFileUrl());
response.setUrlMd5(ossFile.getUrlMd5());
response.setFileKey(ossFile.getFileUuid());
return response;
}
}

View File

@ -0,0 +1,61 @@
package cn.axzo.oss.test;
import cn.axzo.oss.manager.api.dto.request.ServerFileDeleteDto;
import cn.axzo.oss.manager.api.dto.request.ServerFileUploadDto;
import cn.axzo.oss.service.api.FileService;
import cn.axzo.oss.test.base.SpringTestBase;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @program: oss
* @description: 文件服务测试
* @author: mr.jie
* @date: 2021-07-29 18:42
**/
public class FileServiceTest extends SpringTestBase {
@Autowired
private FileService fileService;
@Test
public void deleteTest() {
ServerFileDeleteDto dto = new ServerFileDeleteDto();
dto.setUrl("https://axzo-public.oss-cn-chengdu.aliyuncs.com/109/108046507ad94bf084aa86c07d743799.jpg");
dto.setOperator("zhangsan");
fileService.delete(dto);
}
@Test
public void upload() {
ServerFileUploadDto dto = new ServerFileUploadDto();
dto.setAppCode("test");
dto.setBizScene("1");
dto.setFileName("一个新的文件.xlsx");
File file = new File("/Users/admin/Downloads/表扬人员名称.xlsx");
FileInputStream fileInputStream = null;
byte[] bytes = new byte[(int) file.length()];
try {
fileInputStream = new FileInputStream(file);
fileInputStream.read(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// System.out.println(Arrays.toString(bytes));
dto.setFileContent(bytes);
//
// fileService.upload(dto);
}
}

View File

@ -0,0 +1,18 @@
package cn.axzo.oss.test.base;
import cn.axzo.oss.client.Bootstrap;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @program: oss
* @description: 测试基础
* @author: mr.jie
* @date: 2021-07-29 18:43
**/
@SpringBootTest(classes = Bootstrap.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTestBase {
}

12
pom.xml
View File

@ -37,6 +37,8 @@
<velocity-engine-core.version>2.0</velocity-engine-core.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
<commons-codec.version>1.10</commons-codec.version>
<aliyun-oss.version>3.10.2</aliyun-oss.version>
</properties>
<modules>
@ -157,6 +159,16 @@
<artifactId>xxl-job-core</artifactId>
<version>${xxl.job.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-oss.version}</version>
</dependency>
</dependencies>
</dependencyManagement>