mirror of https://github.com/halo-dev/halo
parent
a4da9a7d58
commit
ab74d8c28b
|
@ -49,6 +49,7 @@ ext {
|
||||||
aliyunSdkVersion = "3.9.1"
|
aliyunSdkVersion = "3.9.1"
|
||||||
baiduSdkVersion = "0.10.36"
|
baiduSdkVersion = "0.10.36"
|
||||||
qcloudSdkVersion = "5.6.25"
|
qcloudSdkVersion = "5.6.25"
|
||||||
|
minioSdkVersion = "7.1.4"
|
||||||
swaggerVersion = "2.9.2"
|
swaggerVersion = "2.9.2"
|
||||||
commonsLangVersion = "3.10"
|
commonsLangVersion = "3.10"
|
||||||
httpclientVersion = "4.5.12"
|
httpclientVersion = "4.5.12"
|
||||||
|
@ -88,6 +89,7 @@ dependencies {
|
||||||
implementation "com.baidubce:bce-java-sdk:$baiduSdkVersion"
|
implementation "com.baidubce:bce-java-sdk:$baiduSdkVersion"
|
||||||
implementation "com.qcloud:cos_api:$qcloudSdkVersion"
|
implementation "com.qcloud:cos_api:$qcloudSdkVersion"
|
||||||
implementation "com.huaweicloud:esdk-obs-java:$huaweiObsVersion"
|
implementation "com.huaweicloud:esdk-obs-java:$huaweiObsVersion"
|
||||||
|
implementation "io.minio:minio:$minioSdkVersion"
|
||||||
implementation "io.springfox:springfox-swagger2:$swaggerVersion"
|
implementation "io.springfox:springfox-swagger2:$swaggerVersion"
|
||||||
implementation "io.springfox:springfox-swagger-ui:$swaggerVersion"
|
implementation "io.springfox:springfox-swagger-ui:$swaggerVersion"
|
||||||
implementation "org.apache.commons:commons-lang3:$commonsLangVersion"
|
implementation "org.apache.commons:commons-lang3:$commonsLangVersion"
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
package run.halo.app.handler.file;
|
||||||
|
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import io.minio.PutObjectArgs;
|
||||||
|
import io.minio.RemoveObjectArgs;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import run.halo.app.exception.FileOperationException;
|
||||||
|
import run.halo.app.model.enums.AttachmentType;
|
||||||
|
import run.halo.app.model.properties.MinioProperties;
|
||||||
|
import run.halo.app.model.support.HaloConst;
|
||||||
|
import run.halo.app.model.support.UploadResult;
|
||||||
|
import run.halo.app.service.OptionService;
|
||||||
|
import run.halo.app.utils.FilenameUtils;
|
||||||
|
import run.halo.app.utils.ImageUtils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MinIO file handler.
|
||||||
|
*
|
||||||
|
* @author Wh1te
|
||||||
|
* @date 2020-10-03
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class MinioFileHandler implements FileHandler {
|
||||||
|
|
||||||
|
private final OptionService optionService;
|
||||||
|
|
||||||
|
public MinioFileHandler(OptionService optionService) {
|
||||||
|
this.optionService = optionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public UploadResult upload(@NotNull MultipartFile file) {
|
||||||
|
Assert.notNull(file, "Multipart file must not be null");
|
||||||
|
// Get config
|
||||||
|
String endpoint = optionService.getByPropertyOfNonNull(MinioProperties.ENDPOINT).toString();
|
||||||
|
String accessKey = optionService.getByPropertyOfNonNull(MinioProperties.ACCESS_KEY).toString();
|
||||||
|
String accessSecret = optionService.getByPropertyOfNonNull(MinioProperties.ACCESS_SECRET).toString();
|
||||||
|
String bucketName = optionService.getByPropertyOfNonNull(MinioProperties.BUCKET_NAME).toString();
|
||||||
|
String source = optionService.getByPropertyOrDefault(MinioProperties.SOURCE, String.class, "");
|
||||||
|
|
||||||
|
endpoint = StringUtils.appendIfMissing(endpoint, HaloConst.URL_SEPARATOR);
|
||||||
|
|
||||||
|
MinioClient minioClient = MinioClient.builder()
|
||||||
|
.endpoint(endpoint)
|
||||||
|
.credentials(accessKey, accessSecret)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String basename = FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
|
||||||
|
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||||
|
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||||
|
String upFilePath = StringUtils.join(StringUtils.isNotBlank(source) ? source + HaloConst.URL_SEPARATOR : "",
|
||||||
|
basename, "_", timestamp, ".", extension);
|
||||||
|
String filePath = StringUtils.join(endpoint, bucketName, HaloConst.URL_SEPARATOR, upFilePath);
|
||||||
|
|
||||||
|
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
|
||||||
|
.contentType(file.getContentType())
|
||||||
|
.bucket(bucketName)
|
||||||
|
.stream(file.getInputStream(), file.getSize(), -1)
|
||||||
|
.object(upFilePath)
|
||||||
|
.build();
|
||||||
|
minioClient.putObject(putObjectArgs);
|
||||||
|
|
||||||
|
UploadResult uploadResult = new UploadResult();
|
||||||
|
uploadResult.setFilename(basename);
|
||||||
|
uploadResult.setFilePath(filePath);
|
||||||
|
uploadResult.setKey(upFilePath);
|
||||||
|
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
|
||||||
|
uploadResult.setSuffix(extension);
|
||||||
|
uploadResult.setSize(file.getSize());
|
||||||
|
|
||||||
|
// Handle thumbnail
|
||||||
|
if (FileHandler.isImageType(uploadResult.getMediaType())) {
|
||||||
|
ImageReader image = ImageUtils.getImageReaderFromFile(file.getInputStream(), extension);
|
||||||
|
assert image != null;
|
||||||
|
uploadResult.setWidth(image.getWidth(0));
|
||||||
|
uploadResult.setHeight(image.getHeight(0));
|
||||||
|
uploadResult.setThumbPath(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uploadResult;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("upload file to MINIO failed", e);
|
||||||
|
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到 MinIO 失败 ", e).setErrorData(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(@NotNull String key) {
|
||||||
|
Assert.notNull(key, "File key must not be blank");
|
||||||
|
|
||||||
|
String endPoint = optionService.getByPropertyOfNonNull(MinioProperties.ENDPOINT).toString();
|
||||||
|
endPoint = StringUtils.appendIfMissing(endPoint, HaloConst.URL_SEPARATOR);
|
||||||
|
|
||||||
|
String accessKey = optionService.getByPropertyOfNonNull(MinioProperties.ACCESS_KEY).toString();
|
||||||
|
String accessSecret = optionService.getByPropertyOfNonNull(MinioProperties.ACCESS_SECRET).toString();
|
||||||
|
String bucketName = optionService.getByPropertyOfNonNull(MinioProperties.BUCKET_NAME).toString();
|
||||||
|
|
||||||
|
MinioClient minioClient = MinioClient.builder()
|
||||||
|
.endpoint(endPoint)
|
||||||
|
.credentials(accessKey, accessSecret)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
minioClient.removeObject(RemoveObjectArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.object(key)
|
||||||
|
.build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("delete MINIO file: [{}] failed", key, e);
|
||||||
|
throw new FileOperationException("附件 " + key + " 从 MinIO 删除失败", e).setErrorData(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttachmentType getAttachmentType() {
|
||||||
|
return AttachmentType.MINIO;
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,12 @@ public enum AttachmentType implements ValueEnum<Integer> {
|
||||||
/**
|
/**
|
||||||
* 华为云
|
* 华为云
|
||||||
*/
|
*/
|
||||||
HUAWEIOBS(7);
|
HUAWEIOBS(7),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MINIO
|
||||||
|
*/
|
||||||
|
MINIO(8);
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package run.halo.app.model.properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minio properties.
|
||||||
|
*
|
||||||
|
* @author Wh1te
|
||||||
|
* @date 2020-10-03
|
||||||
|
*/
|
||||||
|
public enum MinioProperties implements PropertyEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minio endpoint.
|
||||||
|
*/
|
||||||
|
ENDPOINT("minio_endpoint", String.class, ""),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minio bucket name.
|
||||||
|
*/
|
||||||
|
BUCKET_NAME("minio_bucket_name", String.class, ""),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minio access key.
|
||||||
|
*/
|
||||||
|
ACCESS_KEY("minio_access_key", String.class, ""),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minio access secret.
|
||||||
|
*/
|
||||||
|
ACCESS_SECRET("minio_access_secret", String.class, ""),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minio source
|
||||||
|
*/
|
||||||
|
SOURCE("minio_source", String.class, "");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private final Class<?> type;
|
||||||
|
|
||||||
|
private final String defaultValue;
|
||||||
|
|
||||||
|
MinioProperties(String value, Class<?> type, String defaultValue) {
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
if (!PropertyEnum.isSupportedType(type)) {
|
||||||
|
throw new IllegalArgumentException("Unsupported blog property type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = value;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String defaultValue() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue