diff --git a/pom.xml b/pom.xml
index 639f1e815..cbd643638 100755
--- a/pom.xml
+++ b/pom.xml
@@ -45,6 +45,7 @@
4.5.0
4.0.1
7.2.18
+ 3.4.2
0.4.8
0.12.1
3.8.1
@@ -171,6 +172,13 @@
+
+
+ com.aliyun.oss
+ aliyun-sdk-oss
+ ${aliyun-java-sdk.version}
+
+
net.coobird
diff --git a/src/main/java/run/halo/app/handler/file/AliYunFileHandler.java b/src/main/java/run/halo/app/handler/file/AliYunFileHandler.java
new file mode 100644
index 000000000..3fce783f8
--- /dev/null
+++ b/src/main/java/run/halo/app/handler/file/AliYunFileHandler.java
@@ -0,0 +1,125 @@
+package run.halo.app.handler.file;
+
+import cn.hutool.core.lang.Assert;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+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.AliYunProperties;
+import run.halo.app.model.support.UploadResult;
+import run.halo.app.service.OptionService;
+import run.halo.app.utils.FilenameUtils;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * AliYun file handler.
+ * @author MyFaith
+ * @date 2019-04-04 00:06:13
+ */
+@Slf4j
+@Component
+public class AliYunFileHandler implements FileHandler {
+
+ private final OptionService optionService;
+
+ public AliYunFileHandler(OptionService optionService) {
+ this.optionService = optionService;
+ }
+
+ @Override
+ public UploadResult upload(MultipartFile file) {
+ Assert.notNull(file, "Multipart file must not be null");
+
+ // Get config
+ String ossEndPoint = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ENDPOINT);
+ String ossAccessKey = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_KEY);
+ String ossAccessSecret = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_SECRET);
+ String ossBucketName = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_BUCKET_NAME);
+ String ossSource = StringUtils.join("https://", ossBucketName, "." + ossEndPoint);
+
+ // Init OSS client
+ OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKey, ossAccessSecret);
+
+ try {
+ String basename = FilenameUtils.getBasename(file.getOriginalFilename());
+ String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ String timestamp = String.valueOf(new Date().getTime());
+ String upFilePath = StringUtils.join(basename, "_", timestamp, ".", extension);
+ String filePath = StringUtils.join(StringUtils.appendIfMissing(ossSource, "/"), upFilePath);
+
+ // Upload
+ PutObjectResult putObjectResult = ossClient.putObject(ossBucketName, upFilePath, file.getInputStream());
+ if (putObjectResult == null) {
+ throw new FileOperationException("Failed to upload file " + file.getOriginalFilename() + " to AliYun " + upFilePath);
+ }
+
+ // Response result
+ 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())) {
+ BufferedImage image = ImageIO.read(file.getInputStream());
+ uploadResult.setWidth(image.getWidth());
+ uploadResult.setHeight(image.getHeight());
+ uploadResult.setThumbPath(filePath);
+ }
+
+ return uploadResult;
+ } catch (Exception e){
+ e.printStackTrace();
+ } finally {
+ ossClient.shutdown();
+ }
+
+ // Build result
+ UploadResult result = new UploadResult();
+
+ log.info("File: [{}] uploaded successfully", file.getOriginalFilename());
+
+ return result;
+ }
+
+ @Override
+ public void delete(String key) {
+ Assert.notNull(key, "File key must not be blank");
+
+ // Get config
+ String ossEndPoint = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ENDPOINT);
+ String ossAccessKey = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_KEY);
+ String ossAccessSecret = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_ACCESS_SECRET);
+ String ossBucketName = optionService.getByPropertyOfNonNull(AliYunProperties.OSS_BUCKET_NAME);
+ String ossSource = StringUtils.join("https://", ossBucketName, "." + ossEndPoint);
+
+ // Init OSS client
+ OSS ossClient = new OSSClientBuilder().build(ossEndPoint, ossAccessKey, ossAccessSecret);
+
+ try {
+ ossClient.deleteObject(new DeleteObjectsRequest(ossBucketName).withKey(key));
+ } catch (Exception e) {
+ throw new FileOperationException("Failed to delete file " + key + " from AliYun", e);
+ } finally {
+ ossClient.shutdown();
+ }
+ }
+
+ @Override
+ public boolean supportType(AttachmentType type) {
+ return AttachmentType.ALIYUN.equals(type);
+ }
+}
diff --git a/src/main/java/run/halo/app/model/enums/AttachmentType.java b/src/main/java/run/halo/app/model/enums/AttachmentType.java
index fb70ed88e..d2e1c7a5d 100644
--- a/src/main/java/run/halo/app/model/enums/AttachmentType.java
+++ b/src/main/java/run/halo/app/model/enums/AttachmentType.java
@@ -26,7 +26,12 @@ public enum AttachmentType implements ValueEnum {
/**
* sm.ms
*/
- SMMS(3);
+ SMMS(3),
+
+ /**
+ * 阿里云
+ */
+ ALIYUN(4);
private Integer value;
diff --git a/src/main/java/run/halo/app/model/properties/AliYunProperties.java b/src/main/java/run/halo/app/model/properties/AliYunProperties.java
new file mode 100644
index 000000000..c12d3edba
--- /dev/null
+++ b/src/main/java/run/halo/app/model/properties/AliYunProperties.java
@@ -0,0 +1,35 @@
+package run.halo.app.model.properties;
+
+/**
+ * AliYun properties.
+ * @author MyFaith
+ * @date 2019-04-04 00:00:56
+ */
+public enum AliYunProperties implements PropertyEnum {
+ OSS_ENDPOINT("oss_aliyun_endpoint", String.class),
+ OSS_BUCKET_NAME("oss_aliyun_bucket_name", String.class),
+ OSS_ACCESS_KEY("oss_aliyun_access_key", String.class),
+ OSS_ACCESS_SECRET("oss_aliyun_access_secret", String.class);
+
+ private String value;
+ private Class> type;
+
+ AliYunProperties(String value, Class> type) {
+ 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 getValue() {
+ return value;
+ }
+}