diff --git a/src/main/java/cc/ryanc/halo/filehandler/FileHandler.java b/src/main/java/cc/ryanc/halo/filehandler/FileHandler.java new file mode 100644 index 000000000..d7013aea1 --- /dev/null +++ b/src/main/java/cc/ryanc/halo/filehandler/FileHandler.java @@ -0,0 +1,84 @@ +package cc.ryanc.halo.filehandler; + +import cc.ryanc.halo.exception.FileUploadException; +import cc.ryanc.halo.model.enums.AttachmentType; +import cc.ryanc.halo.model.support.UploadResult; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; + +/** + * File handler interface. + * + * @author johnniang + * @date 3/27/19 + */ +public interface FileHandler { + + MediaType IMAGE_TYPE = MediaType.valueOf("image/*"); + + /** + * Uploads file. + * + * @param file multipart file must not be null + * @return upload result + * @throws FileUploadException throws when fail to upload the file + */ + @NonNull + UploadResult upload(@NonNull MultipartFile file); + + /** + * Deletes file. + * + * @param key file key must not be null + */ + boolean delete(@NonNull String key); + + /** + * Checks if the given type is supported. + * + * @param type attachment type + * @return true if supported; false or else + */ + boolean supportType(AttachmentType type); + + + /** + * Check whether media type provided is an image type. + * + * @param mediaType media type provided + * @return true if it is an image type + */ + static boolean isImageType(@Nullable String mediaType) { + return mediaType != null && IMAGE_TYPE.includes(MediaType.valueOf(mediaType)); + } + + + /** + * Check whether media type provided is an image type. + * + * @param mediaType media type provided + * @return true if it is an image type + */ + static boolean isImageType(@Nullable MediaType mediaType) { + return mediaType != null && IMAGE_TYPE.includes(mediaType); + } + + /** + * Normalize directory full name, ensure the end path separator. + * + * @param dir directory full name must not be blank + * @return normalized directory full name with end path separator + */ + @NonNull + static String normalizeDirectory(@NonNull String dir) { + Assert.hasText(dir, "Directory full name must not be blank"); + + return StringUtils.appendIfMissing(dir, File.separator); + } +} diff --git a/src/main/java/cc/ryanc/halo/filehandler/FileHandlerConfiguration.java b/src/main/java/cc/ryanc/halo/filehandler/FileHandlerConfiguration.java new file mode 100644 index 000000000..356a82613 --- /dev/null +++ b/src/main/java/cc/ryanc/halo/filehandler/FileHandlerConfiguration.java @@ -0,0 +1,35 @@ +package cc.ryanc.halo.filehandler; + +import cc.ryanc.halo.config.properties.HaloProperties; +import cc.ryanc.halo.service.OptionService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * File handler configuration. + * + * @author johnniang + * @date 3/27/19 + */ +@Configuration +public class FileHandlerConfiguration { + + @Bean + @ConditionalOnMissingBean + FileHandler localFileHandler(OptionService optionService, HaloProperties haloProperties) { + return new LocalFileHandler(optionService, haloProperties); + } + + @Bean + @ConditionalOnMissingBean + FileHandler qnYunFileHandler(OptionService optionService) { + return new QnYunFileHandler(optionService); + } + + @Bean + @ConditionalOnMissingBean + FileHandler upYunFileHandler(OptionService optionService) { + return new UpYunFileHandler(optionService); + } +} diff --git a/src/main/java/cc/ryanc/halo/filehandler/FileHandlers.java b/src/main/java/cc/ryanc/halo/filehandler/FileHandlers.java new file mode 100644 index 000000000..fd0deaacb --- /dev/null +++ b/src/main/java/cc/ryanc/halo/filehandler/FileHandlers.java @@ -0,0 +1,72 @@ +package cc.ryanc.halo.filehandler; + +import cc.ryanc.halo.exception.FileUploadException; +import cc.ryanc.halo.model.enums.AttachmentType; +import cc.ryanc.halo.model.support.UploadResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * File handler manager. + * + * @author johnniang + * @date 3/27/19 + */ +@Slf4j +@Component +public class FileHandlers { + + /** + * File handler container. + */ + private final Collection fileHandlers = new LinkedList<>(); + + public FileHandlers(ApplicationContext applicationContext) { + // Add all file handler + addFileHandlers(applicationContext.getBeansOfType(FileHandler.class).values()); + } + + public UploadResult upload(MultipartFile file, AttachmentType attachmentType) { + for (FileHandler fileHandler : fileHandlers) { + if (fileHandler.supportType(attachmentType)) { + return fileHandler.upload(file); + } + } + + log.error("There is no available file handle for attachment type: [{}]", attachmentType); + throw new FileUploadException("No available file handler to filehandler the file").setErrorData(attachmentType); + } + + public boolean delete(String key, AttachmentType attachmentType) { + for (FileHandler fileHandler : fileHandlers) { + if (fileHandler.supportType(attachmentType)) { + return fileHandler.delete(key); + } + } + + log.error("There is no available file handle for attachment type: [{}]", attachmentType); + throw new FileUploadException("No available file handler to delete the file").setErrorData(attachmentType); + } + + /** + * Adds file handlers. + * + * @param fileHandlers file handler collection + * @return current file handlers + */ + @NonNull + public FileHandlers addFileHandlers(@Nullable Collection fileHandlers) { + if (!CollectionUtils.isEmpty(fileHandlers)) { + this.fileHandlers.addAll(fileHandlers); + } + return this; + } +} diff --git a/src/main/java/cc/ryanc/halo/filehandler/LocalFileHandler.java b/src/main/java/cc/ryanc/halo/filehandler/LocalFileHandler.java new file mode 100644 index 000000000..b3b920b08 --- /dev/null +++ b/src/main/java/cc/ryanc/halo/filehandler/LocalFileHandler.java @@ -0,0 +1,184 @@ +package cc.ryanc.halo.filehandler; + +import cc.ryanc.halo.config.properties.HaloProperties; +import cc.ryanc.halo.exception.ServiceException; +import cc.ryanc.halo.model.enums.AttachmentType; +import cc.ryanc.halo.model.support.UploadResult; +import cc.ryanc.halo.service.OptionService; +import cc.ryanc.halo.utils.FilenameUtils; +import cc.ryanc.halo.utils.HaloUtils; +import lombok.extern.slf4j.Slf4j; +import net.coobird.thumbnailator.Thumbnails; +import org.springframework.http.MediaType; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Calendar; +import java.util.Objects; + +/** + * Local file handler. + * + * @author johnniang + * @date 3/27/19 + */ +@Slf4j +public class LocalFileHandler implements FileHandler { + + /** + * Upload sub directory. + */ + private final static String UPLOAD_SUB_DIR = "upload"; + + /** + * Thumbnail width. + */ + private final static int THUMB_WIDTH = 256; + + /** + * Thumbnail height. + */ + private final static int THUMB_HEIGHT = 256; + + private final OptionService optionService; + + private final String workDir; + + public LocalFileHandler(OptionService optionService, + HaloProperties haloProperties) { + this.optionService = optionService; + + // Get work dir + workDir = FileHandler.normalizeDirectory(haloProperties.getWorkDir()); + + } + + /** + * Check work directory. + */ + private void checkWorkDir() { + // Get work path + Path workPath = Paths.get(workDir); + + // Check file type + Assert.isTrue(Files.isDirectory(workPath), workDir + " isn't a directory"); + + // Check readable + Assert.isTrue(Files.isReadable(workPath), workDir + " isn't readable"); + + // Check writable + Assert.isTrue(Files.isWritable(workPath), workDir + " isn't writable"); + } + + @Override + public UploadResult upload(MultipartFile file) { + Assert.notNull(file, "Multipart file must not be null"); + + // Get current time + Calendar current = Calendar.getInstance(optionService.getLocale()); + // Get month and day of month + int year = current.get(Calendar.YEAR); + int month = current.get(Calendar.MONTH) + 1; + + // Build directory + String subDir = UPLOAD_SUB_DIR + File.separator + year + File.separator + month + File.separator; + + // Get basename + String basename = FilenameUtils.getBasename(file.getOriginalFilename()) + '-' + HaloUtils.randomUUIDWithoutDash(); + + // Get extension + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + + log.debug("Base name: [{}], extension: [{}] of original filename: [{}]", basename, extension, file.getOriginalFilename()); + + // Build sub file path + String subFilePath = subDir + basename + '.' + extension; + + // Get upload path + Path uploadPath = Paths.get(workDir + subFilePath); + + log.info("Uploading to directory: [{}]", uploadPath.getFileName()); + + try { + // TODO Synchronize here + // Create directory + Files.createDirectories(uploadPath.getParent()); + Files.createFile(uploadPath); + + // Upload this file + file.transferTo(uploadPath); + + // Build upload result + UploadResult uploadResult = new UploadResult(); + uploadResult.setFilename(basename); + uploadResult.setFilePath(subFilePath); + uploadResult.setSuffix(extension); + uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType()))); + uploadResult.setSize(file.getSize()); + + // Check file type + if (FileHandler.isImageType(uploadResult.getMediaType())) { + // Upload a thumbnail + String thumbnailBasename = basename + '-' + "thumbnail"; + String thumbnailSubFilePath = subDir + thumbnailBasename + '.' + extension; + Path thumbnailPath = Paths.get(workDir + thumbnailSubFilePath); + + // Create the thumbnail + Files.createFile(thumbnailPath); + + // Generate thumbnail + generateThumbnail(uploadPath, thumbnailPath); + + // Read as image + BufferedImage image = ImageIO.read(Files.newInputStream(uploadPath)); + + // Set width and height + uploadResult.setWidth(image.getWidth()); + uploadResult.setHeight(image.getHeight()); + + // Set thumb path + uploadResult.setThumbPath(thumbnailSubFilePath); + } + + return uploadResult; + } catch (IOException e) { + log.error("Failed to upload file to local: " + uploadPath.getFileName(), e); + throw new ServiceException("Failed to upload file to local").setErrorData(uploadPath.getFileName()); + } + } + + @Override + public boolean delete(String key) { + return false; + } + + @Override + public boolean supportType(AttachmentType type) { + return AttachmentType.LOCAL.equals(type); + } + + /** + * Generates thumbnail image. + * + * @param imagePath image path must not be null + * @param thumbPath thumbnail path must not be null + * @throws IOException throws if image provided is not valid + */ + private void generateThumbnail(@NonNull Path imagePath, @NonNull Path thumbPath) throws IOException { + Assert.notNull(imagePath, "Image path must not be null"); + Assert.notNull(thumbPath, "Thumb path must not be null"); + + log.info("Generating thumbnail: [{}] for image: [{}]", thumbPath.getFileName(), imagePath.getFileName()); + + // Convert to thumbnail and copy the thumbnail + Thumbnails.of(imagePath.toFile()).size(THUMB_WIDTH, THUMB_HEIGHT).keepAspectRatio(true).toFile(thumbPath.toFile()); + } +} diff --git a/src/main/java/cc/ryanc/halo/filehandler/QnYunFileHandler.java b/src/main/java/cc/ryanc/halo/filehandler/QnYunFileHandler.java new file mode 100644 index 000000000..3b2a1d523 --- /dev/null +++ b/src/main/java/cc/ryanc/halo/filehandler/QnYunFileHandler.java @@ -0,0 +1,129 @@ +package cc.ryanc.halo.filehandler; + +import cc.ryanc.halo.exception.FileUploadException; +import cc.ryanc.halo.model.enums.AttachmentType; +import cc.ryanc.halo.model.enums.QnYunProperties; +import cc.ryanc.halo.model.support.QiNiuPutSet; +import cc.ryanc.halo.model.support.UploadResult; +import cc.ryanc.halo.service.OptionService; +import cc.ryanc.halo.utils.FilenameUtils; +import cc.ryanc.halo.utils.JsonUtils; +import com.qiniu.common.QiniuException; +import com.qiniu.common.Zone; +import com.qiniu.http.Response; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.UploadManager; +import com.qiniu.storage.persistent.FileRecorder; +import com.qiniu.util.Auth; +import com.qiniu.util.StringMap; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +import static cc.ryanc.halo.filehandler.FileHandler.isImageType; + +/** + * Qi niu yun file handler. + * + * @author johnniang + * @date 3/27/19 + */ +@Slf4j +public class QnYunFileHandler implements FileHandler { + + private final OptionService optionService; + + public QnYunFileHandler(OptionService optionService) { + this.optionService = optionService; + } + + @Override + public UploadResult upload(MultipartFile file) { + Assert.notNull(file, "Multipart file must not be null"); + + // Get all config + Zone zone = optionService.getQnYunZone(); + String accessKey = optionService.getByPropertyOfNonNull(QnYunProperties.ACCESS_KEY); + String secretKey = optionService.getByPropertyOfNonNull(QnYunProperties.SECRET_KEY); + String bucket = optionService.getByPropertyOfNonNull(QnYunProperties.BUCKET); + String domain = optionService.getByPropertyOfNonNull(QnYunProperties.DOMAIN); + String smallUrl = optionService.getByPropertyOfNullable(QnYunProperties.SMALL_URL); + + // Create configuration + Configuration configuration = new Configuration(zone); + + // Create auth + Auth auth = Auth.create(accessKey, secretKey); + // Build put plicy + StringMap putPolicy = new StringMap(); + putPolicy.put("returnBody", "{\"size\":$(fsize), " + + "\"width\":$(imageInfo.width), " + + "\"height\":$(imageInfo.height)," + + " \"key\":\"$(key)\", " + + "\"hash\":\"$(etag)\"}"); + // Get upload token + String uploadToken = auth.uploadToken(bucket, null, 3600, putPolicy); + + // Create temp path + Path tmpPath = Paths.get(System.getProperty("java.io.tmpdir"), bucket); + + try { + // Get file recorder for temp directory + FileRecorder fileRecorder = new FileRecorder(tmpPath.toFile()); + // Get upload manager + UploadManager uploadManager = new UploadManager(configuration, fileRecorder); + // Put the file + // TODO May need to set key manually + Response response = uploadManager.put(file.getInputStream(), null, uploadToken, null, null); + + log.debug("QnYun response: [{}]", response.toString()); + log.debug("QnYun response body: [{}]", response.bodyString()); + + response.jsonToObject(QiNiuPutSet.class); + + // Convert response + QiNiuPutSet putSet = JsonUtils.jsonToObject(response.bodyString(), QiNiuPutSet.class); + + // Get file full path + String filePath = StringUtils.appendIfMissing(domain, "/") + putSet.getHash(); + + // Build upload result + UploadResult result = new UploadResult(); + result.setFilename(putSet.getHash()); + result.setFilePath(filePath); + result.setSuffix(FilenameUtils.getExtension(file.getOriginalFilename())); + result.setWidth(putSet.getWidth()); + result.setHeight(putSet.getHeight()); + result.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType()))); + + if (isImageType(result.getMediaType())) { + result.setThumbPath(StringUtils.isBlank(smallUrl) ? filePath : filePath + smallUrl); + } + + return result; + } catch (IOException e) { + if (e instanceof QiniuException) { + log.error("QnYun error response: [{}]", ((QiniuException) e).response); + } + + throw new FileUploadException("Failed to upload file " + file.getOriginalFilename() + " to QnYun", e); + } + } + + @Override + public boolean delete(String key) { + return false; + } + + @Override + public boolean supportType(AttachmentType type) { + return AttachmentType.QNYUN.equals(type); + } +} diff --git a/src/main/java/cc/ryanc/halo/filehandler/UpYunFileHandler.java b/src/main/java/cc/ryanc/halo/filehandler/UpYunFileHandler.java new file mode 100644 index 000000000..8ab015fff --- /dev/null +++ b/src/main/java/cc/ryanc/halo/filehandler/UpYunFileHandler.java @@ -0,0 +1,111 @@ +package cc.ryanc.halo.filehandler; + +import cc.ryanc.halo.exception.FileUploadException; +import cc.ryanc.halo.exception.PropertyFormatException; +import cc.ryanc.halo.model.enums.AttachmentType; +import cc.ryanc.halo.model.enums.UpYunProperties; +import cc.ryanc.halo.model.support.UploadResult; +import cc.ryanc.halo.service.OptionService; +import cc.ryanc.halo.utils.FilenameUtils; +import com.UpYun; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.util.DigestUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.util.Objects; + +/** + * Up Yun file handler. + * + * @author johnniang + * @date 3/27/19 + */ +@Slf4j +public class UpYunFileHandler implements FileHandler { + + private final OptionService optionService; + + public UpYunFileHandler(OptionService optionService) { + this.optionService = optionService; + } + + @Override + public UploadResult upload(MultipartFile file) { + Assert.notNull(file, "Multipart file must not be null"); + + String ossSource = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_SOURCE); + + if (StringUtils.startsWith(ossSource, "/")) { + throw new PropertyFormatException(UpYunProperties.OSS_SOURCE.getValue() + ": " + ossSource + " doesn't start with '/'"); + } + + String ossPassword = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_PASSWORD); + String ossBucket = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_BUCKET); + String ossDomain = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_DOMAIN); + String ossOperator = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_OPERATOR); + // small url can be null + String ossSmallUrl = optionService.getByPropertyOfNullable(UpYunProperties.OSS_SMALL_URL); + + // Create up yun + UpYun upYun = new UpYun(ossBucket, ossOperator, ossPassword); + upYun.setDebug(log.isDebugEnabled()); + upYun.setTimeout(60); + // TODO Provide a property for choosing + upYun.setApiDomain(UpYun.ED_AUTO); + + try { + // Get file basename + String basename = FilenameUtils.getBasename(file.getOriginalFilename()); + // Get file extension + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + // Get md5 value of the file + String md5OfFile = DigestUtils.md5DigestAsHex(file.getInputStream()); + // Build file path + String upFilePath = ossSource + md5OfFile + '.' + extension; + // Set md5Content + upYun.setContentMD5(md5OfFile); + // Write file + boolean uploadSuccess = upYun.writeFile(upFilePath, file.getInputStream(), true, null); + if (!uploadSuccess) { + throw new FileUploadException("Failed to upload file " + file.getOriginalFilename() + " to UpYun " + upFilePath); + } + + String filePath = StringUtils.removeEnd(ossDomain, "/") + upFilePath; + + // Build upload result + UploadResult uploadResult = new UploadResult(); + uploadResult.setFilename(basename); + uploadResult.setFilePath(filePath); + 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(StringUtils.isBlank(ossSmallUrl) ? filePath : filePath + ossSmallUrl); + } + + return uploadResult; + } catch (Exception e) { + throw new FileUploadException("Failed to upload file " + file.getOriginalFilename() + " to UpYun", e); + } + } + + @Override + public boolean delete(String key) { + return false; + } + + @Override + public boolean supportType(AttachmentType type) { + return AttachmentType.UPYUN.equals(type); + } +} diff --git a/src/main/java/cc/ryanc/halo/listener/StartedListener.java b/src/main/java/cc/ryanc/halo/listener/StartedListener.java index e982c1b4c..2a4e1cfca 100644 --- a/src/main/java/cc/ryanc/halo/listener/StartedListener.java +++ b/src/main/java/cc/ryanc/halo/listener/StartedListener.java @@ -143,7 +143,7 @@ public class StartedListener implements ApplicationListener { /** * 服务器 */ - SERVER(0), + LOCAL(0), /** * 又拍云 @@ -21,7 +21,7 @@ public enum AttachmentType implements ValueEnum { /** * 七牛云 */ - QINIUYUN(2); + QNYUN(2); private Integer value; diff --git a/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java b/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java index fa010ece5..9732fb3f8 100644 --- a/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java +++ b/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java @@ -1,6 +1,7 @@ package cc.ryanc.halo.model.enums; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -78,6 +79,7 @@ public interface PropertyEnum extends ValueEnum { * @param property value enum type * @return property enum value or null */ + @Nullable static > T convertToEnum(@NonNull String value, @NonNull Class type) { Assert.hasText(value, "Property value must not be blank"); diff --git a/src/main/java/cc/ryanc/halo/model/support/UploadResult.java b/src/main/java/cc/ryanc/halo/model/support/UploadResult.java index 87c528b4d..64938b66e 100644 --- a/src/main/java/cc/ryanc/halo/model/support/UploadResult.java +++ b/src/main/java/cc/ryanc/halo/model/support/UploadResult.java @@ -1,6 +1,7 @@ package cc.ryanc.halo.model.support; import lombok.Data; +import lombok.ToString; import org.springframework.http.MediaType; /** @@ -10,12 +11,15 @@ import org.springframework.http.MediaType; * @date 3/26/19 */ @Data +@ToString public class UploadResult { private String filename; private String filePath; + private String key; + private String thumbPath; private String suffix; diff --git a/src/main/java/cc/ryanc/halo/service/AttachmentService.java b/src/main/java/cc/ryanc/halo/service/AttachmentService.java index 95a105390..404914832 100644 --- a/src/main/java/cc/ryanc/halo/service/AttachmentService.java +++ b/src/main/java/cc/ryanc/halo/service/AttachmentService.java @@ -1,10 +1,13 @@ package cc.ryanc.halo.service; +import cc.ryanc.halo.exception.FileUploadException; import cc.ryanc.halo.model.dto.AttachmentOutputDTO; import cc.ryanc.halo.model.entity.Attachment; import cc.ryanc.halo.service.base.CrudService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.lang.NonNull; +import org.springframework.web.multipart.MultipartFile; /** @@ -21,4 +24,14 @@ public interface AttachmentService extends CrudService { * @return a page of attachment output dto */ Page pageDtosBy(Pageable pageable); + + /** + * Uploads file. + * + * @param file multipart file must not be null + * @return attachment info + * @throws FileUploadException throws when failed to filehandler the file + */ + @NonNull + Attachment upload(@NonNull MultipartFile file); } diff --git a/src/main/java/cc/ryanc/halo/service/FileService.java b/src/main/java/cc/ryanc/halo/service/FileService.java deleted file mode 100644 index 40d9bd37a..000000000 --- a/src/main/java/cc/ryanc/halo/service/FileService.java +++ /dev/null @@ -1,56 +0,0 @@ -package cc.ryanc.halo.service; - -import cc.ryanc.halo.model.support.UploadResult; -import org.springframework.lang.NonNull; -import org.springframework.web.multipart.MultipartFile; - -/** - * File service interface. - * - * @author johnniang - * @date 3/26/19 - */ -public interface FileService { - - /** - * Upload sub directory. - */ - String UPLOAD_SUB_DIR = "upload"; - - /** - * Thumbnail width. - */ - int THUMB_WIDTH = 256; - - /** - * Thumbnail height. - */ - int THUMB_HEIGHT = 256; - - /** - * Uploads file to local storage. - * - * @param file multipart file must not be null - * @return upload result - */ - @NonNull - UploadResult uploadToLocal(@NonNull MultipartFile file); - - /** - * Uploads file to qi niu yun. - * - * @param file multipart file must not be null - * @return upload result - */ - @NonNull - UploadResult uploadToQnYun(@NonNull MultipartFile file); - - /** - * Uploads file to you pai yun. - * - * @param file multipart file must not be null - * @return upload result - */ - @NonNull - UploadResult uploadToYpYun(@NonNull MultipartFile file); -} diff --git a/src/main/java/cc/ryanc/halo/service/OptionService.java b/src/main/java/cc/ryanc/halo/service/OptionService.java index ea151995c..c7639f9b6 100755 --- a/src/main/java/cc/ryanc/halo/service/OptionService.java +++ b/src/main/java/cc/ryanc/halo/service/OptionService.java @@ -139,7 +139,7 @@ public interface OptionService extends CrudService { * @param property type * @return property value */ - T getByProperty(@NonNull PropertyEnum property, @NonNull Class propertyType, T defaultValue); + T getByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class propertyType, T defaultValue); /** * Gets property value by blog property. @@ -160,7 +160,7 @@ public interface OptionService extends CrudService { * @param property type * @return value */ - T getByKey(@NonNull String key, @NonNull Class valueType, T defaultValue); + T getByKeyOrDefault(@NonNull String key, @NonNull Class valueType, T defaultValue); /** * Gets value by key. @@ -172,6 +172,27 @@ public interface OptionService extends CrudService { */ Optional getByKey(@NonNull String key, @NonNull Class valueType); + /** + * Gets enum value by property. + * + * @param property property must not be blank + * @param valueType enum value type must not be null + * @param enum value type + * @return an optional enum value + */ + > Optional getEnumByProperty(@NonNull PropertyEnum property, @NonNull Class valueType); + + /** + * Gets enum value by property. + * + * @param property property must not be blank + * @param valueType enum value type must not be null + * @param defaultValue default value + * @param enum value type + * @return enum value + */ + > T getEnumByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class valueType, T defaultValue); + /** * Gets post page size. * diff --git a/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java index 6d7e701f5..e1e64e774 100644 --- a/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java +++ b/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java @@ -2,13 +2,21 @@ package cc.ryanc.halo.service.impl; import cc.ryanc.halo.model.dto.AttachmentOutputDTO; import cc.ryanc.halo.model.entity.Attachment; +import cc.ryanc.halo.model.enums.AttachmentType; +import cc.ryanc.halo.model.enums.BlogProperties; +import cc.ryanc.halo.model.support.UploadResult; import cc.ryanc.halo.repository.AttachmentRepository; import cc.ryanc.halo.service.AttachmentService; +import cc.ryanc.halo.service.OptionService; import cc.ryanc.halo.service.base.AbstractCrudService; +import cc.ryanc.halo.filehandler.FileHandlers; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.util.Assert; +import org.springframework.web.multipart.MultipartFile; /** * AttachmentService implementation class @@ -16,14 +24,23 @@ import org.springframework.util.Assert; * @author : RYAN0UP * @date : 2019-03-14 */ +@Slf4j @Service public class AttachmentServiceImpl extends AbstractCrudService implements AttachmentService { private final AttachmentRepository attachmentRepository; - public AttachmentServiceImpl(AttachmentRepository attachmentRepository) { + private final OptionService optionService; + + private final FileHandlers fileHandlers; + + public AttachmentServiceImpl(AttachmentRepository attachmentRepository, + OptionService optionService, + FileHandlers fileHandlers) { super(attachmentRepository); this.attachmentRepository = attachmentRepository; + this.optionService = optionService; + this.fileHandlers = fileHandlers; } @Override @@ -36,4 +53,43 @@ public class AttachmentServiceImpl extends AbstractCrudService new AttachmentOutputDTO().convertFrom(attachment)); } + + @Override + public Attachment upload(MultipartFile file) { + Assert.notNull(file, "Multipart file must not be null"); + + AttachmentType attachmentType = getAttachmentType(); + + // Upload file + UploadResult uploadResult = fileHandlers.upload(file, attachmentType); + + log.debug("Attachment type: [{}]", attachmentType); + log.debug("Upload result: [{}]", uploadResult); + + // Build attachment + Attachment attachment = new Attachment(); + attachment.setName(uploadResult.getFilename()); + attachment.setPath(uploadResult.getFilePath()); + attachment.setFileKey(uploadResult.getKey()); + attachment.setThumbPath(uploadResult.getThumbPath()); + attachment.setMediaType(uploadResult.getMediaType().toString()); + attachment.setSuffix(uploadResult.getSuffix()); + attachment.setWidth(uploadResult.getWidth()); + attachment.setHeight(uploadResult.getHeight()); + attachment.setSize(uploadResult.getSize()); + attachment.setType(attachmentType); + + // Create and return + return create(attachment); + } + + /** + * Get attachment type from options. + * + * @return attachment type + */ + @NonNull + private AttachmentType getAttachmentType() { + return optionService.getEnumByPropertyOrDefault(BlogProperties.ATTACHMENT_TYPE, AttachmentType.class, AttachmentType.LOCAL); + } } diff --git a/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java index 887c3cc29..a345ab91d 100644 --- a/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java +++ b/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java @@ -120,7 +120,7 @@ public class CommentServiceImpl extends AbstractCrudService imple } // Handle comment status - Boolean needAudit = optionService.getByProperty(BlogProperties.NEW_COMMENT_NEED_CHECK, Boolean.class, true); + Boolean needAudit = optionService.getByPropertyOrDefault(BlogProperties.NEW_COMMENT_NEED_CHECK, Boolean.class, true); if (needAudit) { comment.setStatus(CommentStatus.AUDITING); } else { diff --git a/src/main/java/cc/ryanc/halo/service/impl/FileServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/FileServiceImpl.java deleted file mode 100644 index c5a323120..000000000 --- a/src/main/java/cc/ryanc/halo/service/impl/FileServiceImpl.java +++ /dev/null @@ -1,357 +0,0 @@ -package cc.ryanc.halo.service.impl; - -import cc.ryanc.halo.config.properties.HaloProperties; -import cc.ryanc.halo.exception.FileUploadException; -import cc.ryanc.halo.exception.PropertyFormatException; -import cc.ryanc.halo.exception.ServiceException; -import cc.ryanc.halo.model.enums.QnYunProperties; -import cc.ryanc.halo.model.enums.UpYunProperties; -import cc.ryanc.halo.model.support.QiNiuPutSet; -import cc.ryanc.halo.model.support.UploadResult; -import cc.ryanc.halo.service.FileService; -import cc.ryanc.halo.service.OptionService; -import cc.ryanc.halo.utils.FilenameUtils; -import cc.ryanc.halo.utils.HaloUtils; -import cc.ryanc.halo.utils.JsonUtils; -import com.UpYun; -import com.qiniu.common.QiniuException; -import com.qiniu.common.Zone; -import com.qiniu.http.Response; -import com.qiniu.storage.Configuration; -import com.qiniu.storage.UploadManager; -import com.qiniu.storage.persistent.FileRecorder; -import com.qiniu.util.Auth; -import com.qiniu.util.StringMap; -import lombok.extern.slf4j.Slf4j; -import net.coobird.thumbnailator.Thumbnails; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.MediaType; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import org.springframework.util.DigestUtils; -import org.springframework.web.multipart.MultipartFile; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Calendar; -import java.util.Objects; - -/** - * File service implementation. - * - * @author johnniang - * @date 3/26/19 - */ -@Slf4j -@Service -public class FileServiceImpl implements FileService { - - private final OptionService optionService; - - private final String workDir; - - private final MediaType imageType = MediaType.valueOf("image/*"); - - public FileServiceImpl(HaloProperties haloProperties, - OptionService optionService) { - this.optionService = optionService; - - // Get work dir - workDir = normalizeDirectory(haloProperties.getWorkDir()); - - // Check directory - checkWorkDir(); - - log.info("Work directory: [{}]", workDir); - } - - /** - * Check work directory. - */ - private void checkWorkDir() { - // Get work path - Path workPath = Paths.get(workDir); - - // Check file type - Assert.isTrue(Files.isDirectory(workPath), workDir + " isn't a directory"); - - // Check readable - Assert.isTrue(Files.isReadable(workPath), workDir + " isn't readable"); - - // Check writable - Assert.isTrue(Files.isWritable(workPath), workDir + " isn't writable"); - } - - /** - * Normalize directory full name, ensure the end path separator. - * - * @param dir directory full name must not be blank - * @return normalized directory full name with end path separator - */ - @NonNull - private String normalizeDirectory(@NonNull String dir) { - Assert.hasText(dir, "Directory full name must not be blank"); - - return StringUtils.appendIfMissing(dir, File.separator); - } - - @Override - public UploadResult uploadToLocal(MultipartFile file) { - Assert.notNull(file, "Multipart file must not be null"); - - // Get current time - Calendar current = Calendar.getInstance(optionService.getLocale()); - // Get month and day of month - int year = current.get(Calendar.YEAR); - int month = current.get(Calendar.MONTH) + 1; - - // Build directory - String subDir = UPLOAD_SUB_DIR + File.separator + year + File.separator + month + File.separator; - - // Get basename - String basename = FilenameUtils.getBasename(file.getOriginalFilename()) + '-' + HaloUtils.randomUUIDWithoutDash(); - - // Get extension - String extension = FilenameUtils.getExtension(file.getOriginalFilename()); - - log.debug("Base name: [{}], extension: [{}] of original filename: [{}]", basename, extension, file.getOriginalFilename()); - - // Build sub file path - String subFilePath = subDir + basename + '.' + extension; - - // Get upload path - Path uploadPath = Paths.get(workDir + subFilePath); - - log.info("Uploading to directory: [{}]", uploadPath.getFileName()); - - try { - // TODO Synchronize here - // Create directory - Files.createDirectories(uploadPath.getParent()); - Files.createFile(uploadPath); - - // Upload this file - file.transferTo(uploadPath); - - // Build upload result - UploadResult uploadResult = new UploadResult(); - uploadResult.setFilename(basename); - uploadResult.setFilePath(subFilePath); - uploadResult.setSuffix(extension); - uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType()))); - uploadResult.setSize(file.getSize()); - - // Check file type - if (isImageType(uploadResult.getMediaType())) { - // Upload a thumbnail - String thumbnailBasename = basename + '-' + "thumbnail"; - String thumbnailSubFilePath = subDir + thumbnailBasename + '.' + extension; - Path thumbnailPath = Paths.get(workDir + thumbnailSubFilePath); - - // Create the thumbnail - Files.createFile(thumbnailPath); - - // Generate thumbnail - generateThumbnail(uploadPath, thumbnailPath); - - // Set thumb path - uploadResult.setThumbPath(thumbnailSubFilePath); - - // Read as image - BufferedImage image = ImageIO.read(Files.newInputStream(uploadPath)); - - // Set width and height - uploadResult.setWidth(image.getWidth()); - uploadResult.setHeight(image.getHeight()); - } - - return uploadResult; - } catch (IOException e) { - log.error("Failed to upload file to local: " + uploadPath.getFileName(), e); - throw new ServiceException("Failed to upload file to local").setErrorData(uploadPath.getFileName()); - } - } - - @Override - public UploadResult uploadToQnYun(MultipartFile file) { - Assert.notNull(file, "Multipart file must not be null"); - - // Get all config - Zone zone = optionService.getQnYunZone(); - String accessKey = optionService.getByPropertyOfNonNull(QnYunProperties.ACCESS_KEY); - String secretKey = optionService.getByPropertyOfNonNull(QnYunProperties.SECRET_KEY); - String bucket = optionService.getByPropertyOfNonNull(QnYunProperties.BUCKET); - String domain = optionService.getByPropertyOfNonNull(QnYunProperties.DOMAIN); - String smallUrl = optionService.getByPropertyOfNullable(QnYunProperties.SMALL_URL); - - // Create configuration - Configuration configuration = new Configuration(zone); - - // Create auth - Auth auth = Auth.create(accessKey, secretKey); - // Build put plicy - StringMap putPolicy = new StringMap(); - putPolicy.put("returnBody", "{\"size\":$(fsize), " + - "\"width\":$(imageInfo.width), " + - "\"height\":$(imageInfo.height)," + - " \"key\":\"$(key)\", " + - "\"hash\":\"$(etag)\"}"); - // Get upload token - String uploadToken = auth.uploadToken(bucket, null, 3600, putPolicy); - - // Create temp path - Path tmpPath = Paths.get(System.getProperty("java.io.tmpdir"), bucket); - - try { - // Get file recorder for temp directory - FileRecorder fileRecorder = new FileRecorder(tmpPath.toFile()); - // Get upload manager - UploadManager uploadManager = new UploadManager(configuration, fileRecorder); - // Put the file - // TODO May need to set key manually - Response response = uploadManager.put(file.getInputStream(), null, uploadToken, null, null); - - log.debug("QnYun response: [{}]", response.toString()); - log.debug("QnYun response body: [{}]", response.bodyString()); - - response.jsonToObject(QiNiuPutSet.class); - - // Convert response - QiNiuPutSet putSet = JsonUtils.jsonToObject(response.bodyString(), QiNiuPutSet.class); - - // Get file full path - String filePath = StringUtils.appendIfMissing(domain, "/") + putSet.getHash(); - - // Build upload result - UploadResult result = new UploadResult(); - result.setFilename(putSet.getHash()); - result.setFilePath(filePath); - result.setSuffix(FilenameUtils.getExtension(file.getOriginalFilename())); - result.setWidth(putSet.getWidth()); - result.setHeight(putSet.getHeight()); - result.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType()))); - - if (isImageType(result.getMediaType())) { - result.setThumbPath(StringUtils.isBlank(smallUrl) ? filePath : filePath + smallUrl); - } - - return result; - } catch (IOException e) { - if (e instanceof QiniuException) { - log.error("QnYun error response: [{}]", ((QiniuException) e).response); - } - - throw new FileUploadException("Failed to upload file " + file.getOriginalFilename() + " to QnYun", e); - } - } - - @Override - public UploadResult uploadToYpYun(MultipartFile file) { - Assert.notNull(file, "Multipart file must not be null"); - - String ossSource = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_SOURCE); - - if (StringUtils.startsWith(ossSource, "/")) { - throw new PropertyFormatException(UpYunProperties.OSS_SOURCE.getValue() + ": " + ossSource + " doesn't start with '/'"); - } - - String ossPassword = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_PASSWORD); - String ossBucket = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_BUCKET); - String ossDomain = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_DOMAIN); - String ossOperator = optionService.getByPropertyOfNonNull(UpYunProperties.OSS_OPERATOR); - // small url can be null - String ossSmallUrl = optionService.getByPropertyOfNullable(UpYunProperties.OSS_SMALL_URL); - - // Create up yun - UpYun upYun = new UpYun(ossBucket, ossOperator, ossPassword); - upYun.setDebug(log.isDebugEnabled()); - upYun.setTimeout(60); - // TODO Provide a property for choosing - upYun.setApiDomain(UpYun.ED_AUTO); - - try { - // Get file basename - String basename = FilenameUtils.getBasename(file.getOriginalFilename()); - // Get file extension - String extension = FilenameUtils.getExtension(file.getOriginalFilename()); - // Get md5 value of the file - String md5OfFile = DigestUtils.md5DigestAsHex(file.getInputStream()); - // Build file path - String upFilePath = ossSource + md5OfFile + '.' + extension; - // Set md5Content - upYun.setContentMD5(md5OfFile); - // Write file - boolean uploadSuccess = upYun.writeFile(upFilePath, file.getInputStream(), true, null); - if (!uploadSuccess) { - throw new FileUploadException("Failed to upload file " + file.getOriginalFilename() + " to UpYun " + upFilePath); - } - - String filePath = StringUtils.removeEnd(ossDomain, "/") + upFilePath; - - // Build upload result - UploadResult uploadResult = new UploadResult(); - uploadResult.setFilename(basename); - uploadResult.setFilePath(filePath); - uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType()))); - uploadResult.setSuffix(extension); - uploadResult.setSize(file.getSize()); - - // Handle thumbnail - if (isImageType(uploadResult.getMediaType())) { - BufferedImage image = ImageIO.read(file.getInputStream()); - uploadResult.setWidth(image.getWidth()); - uploadResult.setHeight(image.getHeight()); - uploadResult.setThumbPath(StringUtils.isBlank(ossSmallUrl) ? filePath : filePath + ossSmallUrl); - } - - return uploadResult; - } catch (Exception e) { - throw new FileUploadException("Failed to upload file " + file.getOriginalFilename() + " to UpYun", e); - } - } - - /** - * Generates thumbnail image. - * - * @param imagePath image path must not be null - * @param thumbPath thumbnail path must not be null - * @throws IOException throws if image provided is not valid - */ - private void generateThumbnail(@NonNull Path imagePath, @NonNull Path thumbPath) throws IOException { - Assert.notNull(imagePath, "Image path must not be null"); - Assert.notNull(thumbPath, "Thumb path must not be null"); - - log.info("Generating thumbnail: [{}] for image: [{}]", thumbPath.getFileName(), imagePath.getFileName()); - - // Convert to thumbnail and copy the thumbnail - Thumbnails.of(imagePath.toFile()).size(THUMB_WIDTH, THUMB_HEIGHT).keepAspectRatio(true).toFile(thumbPath.toFile()); - } - - /** - * Check whether media type provided is an image type. - * - * @param mediaType media type provided - * @return true if it is an image type - */ - private boolean isImageType(@Nullable String mediaType) { - return mediaType != null && imageType.includes(MediaType.valueOf(mediaType)); - } - - - /** - * Check whether media type provided is an image type. - * - * @param mediaType media type provided - * @return true if it is an image type - */ - private boolean isImageType(@Nullable MediaType mediaType) { - return mediaType != null && imageType.includes(mediaType); - } -} diff --git a/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java index fe4e1f79a..77689222f 100644 --- a/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java +++ b/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java @@ -167,7 +167,7 @@ public class OptionServiceImpl extends AbstractCrudService impl } @Override - public T getByProperty(PropertyEnum property, Class propertyType, T defaultValue) { + public T getByPropertyOrDefault(PropertyEnum property, Class propertyType, T defaultValue) { Assert.notNull(property, "Blog property must not be null"); return getByProperty(property, propertyType).orElse(defaultValue); @@ -179,7 +179,7 @@ public class OptionServiceImpl extends AbstractCrudService impl } @Override - public T getByKey(String key, Class valueType, T defaultValue) { + public T getByKeyOrDefault(String key, Class valueType, T defaultValue) { return getByKey(key, valueType).orElse(defaultValue); } @@ -188,10 +188,20 @@ public class OptionServiceImpl extends AbstractCrudService impl return getByKey(key).map(value -> PropertyEnum.convertTo(value, valueType)); } + @Override + public > Optional getEnumByProperty(PropertyEnum property, Class valueType) { + return getByProperty(property).map(value -> PropertyEnum.convertToEnum(value, valueType)); + } + + @Override + public > T getEnumByPropertyOrDefault(PropertyEnum property, Class valueType, T defaultValue) { + return getEnumByProperty(property, valueType).orElse(defaultValue); + } + @Override public int getPostPageSize() { try { - return getByProperty(BlogProperties.INDEX_POSTS, Integer.class, DEFAULT_COMMENT_PAGE_SIZE); + return getByPropertyOrDefault(BlogProperties.INDEX_POSTS, Integer.class, DEFAULT_COMMENT_PAGE_SIZE); } catch (NumberFormatException e) { log.error(BlogProperties.INDEX_POSTS + " option is not a number format", e); return DEFAULT_POST_PAGE_SIZE; @@ -201,7 +211,7 @@ public class OptionServiceImpl extends AbstractCrudService impl @Override public int getCommentPageSize() { try { - return getByProperty(BlogProperties.INDEX_COMMENTS, Integer.class, DEFAULT_COMMENT_PAGE_SIZE); + return getByPropertyOrDefault(BlogProperties.INDEX_COMMENTS, Integer.class, DEFAULT_COMMENT_PAGE_SIZE); } catch (NumberFormatException e) { log.error(BlogProperties.INDEX_COMMENTS + " option is not a number format", e); return DEFAULT_COMMENT_PAGE_SIZE; @@ -211,7 +221,7 @@ public class OptionServiceImpl extends AbstractCrudService impl @Override public int getRssPageSize() { try { - return getByProperty(BlogProperties.RSS_POSTS, Integer.class, DEFAULT_COMMENT_PAGE_SIZE); + return getByPropertyOrDefault(BlogProperties.RSS_POSTS, Integer.class, DEFAULT_COMMENT_PAGE_SIZE); } catch (NumberFormatException e) { log.error(BlogProperties.RSS_POSTS + " setting is not a number format", e); return DEFAULT_RSS_PAGE_SIZE; diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/api/AttachmentController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/api/AttachmentController.java index 34437d57d..3e6aa914a 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/api/AttachmentController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/api/AttachmentController.java @@ -1,9 +1,7 @@ package cc.ryanc.halo.web.controller.admin.api; import cc.ryanc.halo.model.dto.AttachmentOutputDTO; -import cc.ryanc.halo.model.support.UploadResult; import cc.ryanc.halo.service.AttachmentService; -import cc.ryanc.halo.service.FileService; import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -25,12 +23,9 @@ public class AttachmentController { private final AttachmentService attachmentService; - private final FileService fileService; - public AttachmentController(AttachmentService attachmentService, - FileService fileService) { + public AttachmentController(AttachmentService attachmentService) { this.attachmentService = attachmentService; - this.fileService = fileService; } /** @@ -68,8 +63,7 @@ public class AttachmentController { } @PostMapping("upload") - public UploadResult uploadAttachment(@RequestParam("file") MultipartFile file) { - // TODO Just for test - return fileService.uploadToLocal(file); + public AttachmentOutputDTO uploadAttachment(@RequestParam("file") MultipartFile file) { + return new AttachmentOutputDTO().convertFrom(attachmentService.upload(file)); } } diff --git a/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java b/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java index 112356db4..be2e69d60 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java @@ -7,7 +7,6 @@ import cc.ryanc.halo.model.enums.BlogProperties; import cc.ryanc.halo.model.params.InstallParam; import cc.ryanc.halo.model.support.BaseResponse; import cc.ryanc.halo.service.*; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import freemarker.template.Configuration; import lombok.extern.slf4j.Slf4j; @@ -168,12 +167,12 @@ public class InstallController { properties.put(BlogProperties.BLOG_TITLE, installParam.getTitle()); properties.put(BlogProperties.BLOG_URL, installParam.getUrl()); properties.put(BlogProperties.THEME, DEFAULT_THEME_NAME); - properties.put(BlogProperties.BLOG_START, DateUtil.format(DateUtil.date(), "yyyy-MM-dd")); + properties.put(BlogProperties.BLOG_START, String.valueOf(System.currentTimeMillis())); properties.put(BlogProperties.SMTP_EMAIL_ENABLE, Boolean.FALSE.toString()); properties.put(BlogProperties.NEW_COMMENT_NOTICE, Boolean.FALSE.toString()); properties.put(BlogProperties.COMMENT_PASS_NOTICE, Boolean.FALSE.toString()); properties.put(BlogProperties.COMMENT_REPLY_NOTICE, Boolean.FALSE.toString()); - properties.put(BlogProperties.ATTACHMENT_TYPE, AttachmentType.SERVER.getValue().toString()); + properties.put(BlogProperties.ATTACHMENT_TYPE, AttachmentType.LOCAL.getValue().toString()); // Create properties optionService.saveProperties(properties, "system"); diff --git a/src/test/java/cc/ryanc/halo/model/enums/AttachmentTypeTest.java b/src/test/java/cc/ryanc/halo/model/enums/AttachmentTypeTest.java index 465a8259c..ff45099da 100644 --- a/src/test/java/cc/ryanc/halo/model/enums/AttachmentTypeTest.java +++ b/src/test/java/cc/ryanc/halo/model/enums/AttachmentTypeTest.java @@ -24,9 +24,9 @@ public class AttachmentTypeTest { @Test public void conversionTest() { - assertThat(conversionService.convert("SERVER", AttachmentType.class), equalTo(AttachmentType.SERVER)); - assertThat(conversionService.convert("server", AttachmentType.class), equalTo(AttachmentType.SERVER)); - assertThat(conversionService.convert("Server", AttachmentType.class), equalTo(AttachmentType.SERVER)); - assertThat(conversionService.convert("SerVer", AttachmentType.class), equalTo(AttachmentType.SERVER)); + assertThat(conversionService.convert("SERVER", AttachmentType.class), equalTo(AttachmentType.LOCAL)); + assertThat(conversionService.convert("server", AttachmentType.class), equalTo(AttachmentType.LOCAL)); + assertThat(conversionService.convert("Server", AttachmentType.class), equalTo(AttachmentType.LOCAL)); + assertThat(conversionService.convert("SerVer", AttachmentType.class), equalTo(AttachmentType.LOCAL)); } } \ No newline at end of file