mirror of https://github.com/halo-dev/halo
Merge remote-tracking branch 'origin/v1' into v1
commit
1d26aad03e
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<FileHandler> 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<FileHandler> fileHandlers) {
|
||||||
|
if (!CollectionUtils.isEmpty(fileHandlers)) {
|
||||||
|
this.fileHandlers.addAll(fileHandlers);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -143,7 +143,7 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
|
||||||
*/
|
*/
|
||||||
private void initThemes() {
|
private void initThemes() {
|
||||||
// Whether the blog has initialized
|
// Whether the blog has initialized
|
||||||
Boolean isInstalled = optionService.getByProperty(BlogProperties.IS_INSTALL, Boolean.class, false);
|
Boolean isInstalled = optionService.getByPropertyOrDefault(BlogProperties.IS_INSTALL, Boolean.class, false);
|
||||||
try {
|
try {
|
||||||
if (isInstalled) {
|
if (isInstalled) {
|
||||||
// Skip
|
// Skip
|
||||||
|
|
|
@ -19,6 +19,8 @@ public class AttachmentOutputDTO implements OutputConverter<AttachmentOutputDTO,
|
||||||
|
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
private String fileKey;
|
||||||
|
|
||||||
private String thumbPath;
|
private String thumbPath;
|
||||||
|
|
||||||
private String mediaType;
|
private String mediaType;
|
||||||
|
|
|
@ -41,6 +41,12 @@ public class Attachment extends BaseEntity {
|
||||||
@Column(name = "path", columnDefinition = "varchar(1023) not null")
|
@Column(name = "path", columnDefinition = "varchar(1023) not null")
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File key: oss file key or local file key (Just for deleting)
|
||||||
|
*/
|
||||||
|
@Column(name = "file_key", columnDefinition = "varchar(2047) default ''")
|
||||||
|
private String fileKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缩略图路径
|
* 缩略图路径
|
||||||
*/
|
*/
|
||||||
|
@ -88,6 +94,10 @@ public class Attachment extends BaseEntity {
|
||||||
super.prePersist();
|
super.prePersist();
|
||||||
id = null;
|
id = null;
|
||||||
|
|
||||||
|
if (fileKey == null) {
|
||||||
|
fileKey = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (thumbPath == null) {
|
if (thumbPath == null) {
|
||||||
thumbPath = "";
|
thumbPath = "";
|
||||||
}
|
}
|
||||||
|
@ -105,9 +115,7 @@ public class Attachment extends BaseEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
type = AttachmentType.SERVER;
|
type = AttachmentType.LOCAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ public enum AttachmentType implements ValueEnum<Integer> {
|
||||||
/**
|
/**
|
||||||
* 服务器
|
* 服务器
|
||||||
*/
|
*/
|
||||||
SERVER(0),
|
LOCAL(0),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 又拍云
|
* 又拍云
|
||||||
|
@ -21,7 +21,7 @@ public enum AttachmentType implements ValueEnum<Integer> {
|
||||||
/**
|
/**
|
||||||
* 七牛云
|
* 七牛云
|
||||||
*/
|
*/
|
||||||
QINIUYUN(2);
|
QNYUN(2);
|
||||||
|
|
||||||
private Integer value;
|
private Integer value;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cc.ryanc.halo.model.enums;
|
package cc.ryanc.halo.model.enums;
|
||||||
|
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,6 +79,7 @@ public interface PropertyEnum extends ValueEnum<String> {
|
||||||
* @param <T> property value enum type
|
* @param <T> property value enum type
|
||||||
* @return property enum value or null
|
* @return property enum value or null
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
static <T extends Enum<T>> T convertToEnum(@NonNull String value, @NonNull Class<T> type) {
|
static <T extends Enum<T>> T convertToEnum(@NonNull String value, @NonNull Class<T> type) {
|
||||||
Assert.hasText(value, "Property value must not be blank");
|
Assert.hasText(value, "Property value must not be blank");
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cc.ryanc.halo.model.support;
|
package cc.ryanc.halo.model.support;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.ToString;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,12 +11,15 @@ import org.springframework.http.MediaType;
|
||||||
* @date 3/26/19
|
* @date 3/26/19
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@ToString
|
||||||
public class UploadResult {
|
public class UploadResult {
|
||||||
|
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
private String filePath;
|
private String filePath;
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
|
||||||
private String thumbPath;
|
private String thumbPath;
|
||||||
|
|
||||||
private String suffix;
|
private String suffix;
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package cc.ryanc.halo.service;
|
package cc.ryanc.halo.service;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.exception.FileUploadException;
|
||||||
import cc.ryanc.halo.model.dto.AttachmentOutputDTO;
|
import cc.ryanc.halo.model.dto.AttachmentOutputDTO;
|
||||||
import cc.ryanc.halo.model.entity.Attachment;
|
import cc.ryanc.halo.model.entity.Attachment;
|
||||||
import cc.ryanc.halo.service.base.CrudService;
|
import cc.ryanc.halo.service.base.CrudService;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
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<Attachment, Integer> {
|
||||||
* @return a page of attachment output dto
|
* @return a page of attachment output dto
|
||||||
*/
|
*/
|
||||||
Page<AttachmentOutputDTO> pageDtosBy(Pageable pageable);
|
Page<AttachmentOutputDTO> 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -139,7 +139,7 @@ public interface OptionService extends CrudService<Option, Integer> {
|
||||||
* @param <T> property type
|
* @param <T> property type
|
||||||
* @return property value
|
* @return property value
|
||||||
*/
|
*/
|
||||||
<T> T getByProperty(@NonNull PropertyEnum property, @NonNull Class<T> propertyType, T defaultValue);
|
<T> T getByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class<T> propertyType, T defaultValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets property value by blog property.
|
* Gets property value by blog property.
|
||||||
|
@ -160,7 +160,7 @@ public interface OptionService extends CrudService<Option, Integer> {
|
||||||
* @param <T> property type
|
* @param <T> property type
|
||||||
* @return value
|
* @return value
|
||||||
*/
|
*/
|
||||||
<T> T getByKey(@NonNull String key, @NonNull Class<T> valueType, T defaultValue);
|
<T> T getByKeyOrDefault(@NonNull String key, @NonNull Class<T> valueType, T defaultValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets value by key.
|
* Gets value by key.
|
||||||
|
@ -172,6 +172,27 @@ public interface OptionService extends CrudService<Option, Integer> {
|
||||||
*/
|
*/
|
||||||
<T> Optional<T> getByKey(@NonNull String key, @NonNull Class<T> valueType);
|
<T> Optional<T> getByKey(@NonNull String key, @NonNull Class<T> valueType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets enum value by property.
|
||||||
|
*
|
||||||
|
* @param property property must not be blank
|
||||||
|
* @param valueType enum value type must not be null
|
||||||
|
* @param <T> enum value type
|
||||||
|
* @return an optional enum value
|
||||||
|
*/
|
||||||
|
<T extends Enum<T>> Optional<T> getEnumByProperty(@NonNull PropertyEnum property, @NonNull Class<T> 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 <T> enum value type
|
||||||
|
* @return enum value
|
||||||
|
*/
|
||||||
|
<T extends Enum<T>> T getEnumByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class<T> valueType, T defaultValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets post page size.
|
* Gets post page size.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,13 +2,21 @@ package cc.ryanc.halo.service.impl;
|
||||||
|
|
||||||
import cc.ryanc.halo.model.dto.AttachmentOutputDTO;
|
import cc.ryanc.halo.model.dto.AttachmentOutputDTO;
|
||||||
import cc.ryanc.halo.model.entity.Attachment;
|
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.repository.AttachmentRepository;
|
||||||
import cc.ryanc.halo.service.AttachmentService;
|
import cc.ryanc.halo.service.AttachmentService;
|
||||||
|
import cc.ryanc.halo.service.OptionService;
|
||||||
import cc.ryanc.halo.service.base.AbstractCrudService;
|
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.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AttachmentService implementation class
|
* AttachmentService implementation class
|
||||||
|
@ -16,14 +24,23 @@ import org.springframework.util.Assert;
|
||||||
* @author : RYAN0UP
|
* @author : RYAN0UP
|
||||||
* @date : 2019-03-14
|
* @date : 2019-03-14
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class AttachmentServiceImpl extends AbstractCrudService<Attachment, Integer> implements AttachmentService {
|
public class AttachmentServiceImpl extends AbstractCrudService<Attachment, Integer> implements AttachmentService {
|
||||||
|
|
||||||
private final AttachmentRepository attachmentRepository;
|
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);
|
super(attachmentRepository);
|
||||||
this.attachmentRepository = attachmentRepository;
|
this.attachmentRepository = attachmentRepository;
|
||||||
|
this.optionService = optionService;
|
||||||
|
this.fileHandlers = fileHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,4 +53,43 @@ public class AttachmentServiceImpl extends AbstractCrudService<Attachment, Integ
|
||||||
// Convert and return
|
// Convert and return
|
||||||
return attachmentPage.map(attachment -> new AttachmentOutputDTO().convertFrom(attachment));
|
return attachmentPage.map(attachment -> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ public class CommentServiceImpl extends AbstractCrudService<Comment, Long> imple
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle comment status
|
// 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) {
|
if (needAudit) {
|
||||||
comment.setStatus(CommentStatus.AUDITING);
|
comment.setStatus(CommentStatus.AUDITING);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -167,7 +167,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getByProperty(PropertyEnum property, Class<T> propertyType, T defaultValue) {
|
public <T> T getByPropertyOrDefault(PropertyEnum property, Class<T> propertyType, T defaultValue) {
|
||||||
Assert.notNull(property, "Blog property must not be null");
|
Assert.notNull(property, "Blog property must not be null");
|
||||||
|
|
||||||
return getByProperty(property, propertyType).orElse(defaultValue);
|
return getByProperty(property, propertyType).orElse(defaultValue);
|
||||||
|
@ -179,7 +179,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getByKey(String key, Class<T> valueType, T defaultValue) {
|
public <T> T getByKeyOrDefault(String key, Class<T> valueType, T defaultValue) {
|
||||||
return getByKey(key, valueType).orElse(defaultValue);
|
return getByKey(key, valueType).orElse(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,10 +188,20 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
return getByKey(key).map(value -> PropertyEnum.convertTo(value, valueType));
|
return getByKey(key).map(value -> PropertyEnum.convertTo(value, valueType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Enum<T>> Optional<T> getEnumByProperty(PropertyEnum property, Class<T> valueType) {
|
||||||
|
return getByProperty(property).map(value -> PropertyEnum.convertToEnum(value, valueType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Enum<T>> T getEnumByPropertyOrDefault(PropertyEnum property, Class<T> valueType, T defaultValue) {
|
||||||
|
return getEnumByProperty(property, valueType).orElse(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPostPageSize() {
|
public int getPostPageSize() {
|
||||||
try {
|
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) {
|
} catch (NumberFormatException e) {
|
||||||
log.error(BlogProperties.INDEX_POSTS + " option is not a number format", e);
|
log.error(BlogProperties.INDEX_POSTS + " option is not a number format", e);
|
||||||
return DEFAULT_POST_PAGE_SIZE;
|
return DEFAULT_POST_PAGE_SIZE;
|
||||||
|
@ -201,7 +211,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
@Override
|
@Override
|
||||||
public int getCommentPageSize() {
|
public int getCommentPageSize() {
|
||||||
try {
|
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) {
|
} catch (NumberFormatException e) {
|
||||||
log.error(BlogProperties.INDEX_COMMENTS + " option is not a number format", e);
|
log.error(BlogProperties.INDEX_COMMENTS + " option is not a number format", e);
|
||||||
return DEFAULT_COMMENT_PAGE_SIZE;
|
return DEFAULT_COMMENT_PAGE_SIZE;
|
||||||
|
@ -211,7 +221,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
@Override
|
@Override
|
||||||
public int getRssPageSize() {
|
public int getRssPageSize() {
|
||||||
try {
|
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) {
|
} catch (NumberFormatException e) {
|
||||||
log.error(BlogProperties.RSS_POSTS + " setting is not a number format", e);
|
log.error(BlogProperties.RSS_POSTS + " setting is not a number format", e);
|
||||||
return DEFAULT_RSS_PAGE_SIZE;
|
return DEFAULT_RSS_PAGE_SIZE;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package cc.ryanc.halo.web.controller.admin.api;
|
package cc.ryanc.halo.web.controller.admin.api;
|
||||||
|
|
||||||
import cc.ryanc.halo.model.dto.AttachmentOutputDTO;
|
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.AttachmentService;
|
||||||
import cc.ryanc.halo.service.FileService;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
@ -25,12 +23,9 @@ public class AttachmentController {
|
||||||
|
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
|
|
||||||
private final FileService fileService;
|
|
||||||
|
|
||||||
public AttachmentController(AttachmentService attachmentService,
|
public AttachmentController(AttachmentService attachmentService) {
|
||||||
FileService fileService) {
|
|
||||||
this.attachmentService = attachmentService;
|
this.attachmentService = attachmentService;
|
||||||
this.fileService = fileService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,8 +63,7 @@ public class AttachmentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("upload")
|
@PostMapping("upload")
|
||||||
public UploadResult uploadAttachment(@RequestParam("file") MultipartFile file) {
|
public AttachmentOutputDTO uploadAttachment(@RequestParam("file") MultipartFile file) {
|
||||||
// TODO Just for test
|
return new AttachmentOutputDTO().convertFrom(attachmentService.upload(file));
|
||||||
return fileService.uploadToLocal(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import cc.ryanc.halo.model.enums.BlogProperties;
|
||||||
import cc.ryanc.halo.model.params.InstallParam;
|
import cc.ryanc.halo.model.params.InstallParam;
|
||||||
import cc.ryanc.halo.model.support.BaseResponse;
|
import cc.ryanc.halo.model.support.BaseResponse;
|
||||||
import cc.ryanc.halo.service.*;
|
import cc.ryanc.halo.service.*;
|
||||||
import cn.hutool.core.date.DateUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import freemarker.template.Configuration;
|
import freemarker.template.Configuration;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -168,12 +167,12 @@ public class InstallController {
|
||||||
properties.put(BlogProperties.BLOG_TITLE, installParam.getTitle());
|
properties.put(BlogProperties.BLOG_TITLE, installParam.getTitle());
|
||||||
properties.put(BlogProperties.BLOG_URL, installParam.getUrl());
|
properties.put(BlogProperties.BLOG_URL, installParam.getUrl());
|
||||||
properties.put(BlogProperties.THEME, DEFAULT_THEME_NAME);
|
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.SMTP_EMAIL_ENABLE, Boolean.FALSE.toString());
|
||||||
properties.put(BlogProperties.NEW_COMMENT_NOTICE, 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_PASS_NOTICE, Boolean.FALSE.toString());
|
||||||
properties.put(BlogProperties.COMMENT_REPLY_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
|
// Create properties
|
||||||
optionService.saveProperties(properties, "system");
|
optionService.saveProperties(properties, "system");
|
||||||
|
|
|
@ -24,9 +24,9 @@ public class AttachmentTypeTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void conversionTest() {
|
public void conversionTest() {
|
||||||
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.SERVER));
|
assertThat(conversionService.convert("server", AttachmentType.class), equalTo(AttachmentType.LOCAL));
|
||||||
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.SERVER));
|
assertThat(conversionService.convert("SerVer", AttachmentType.class), equalTo(AttachmentType.LOCAL));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue