refactor: the naming method of uploaded attachments (#1500)

* feat: Refactor the naming method of uploaded attachments

* refactor: attachment upload parameter

* refactor: 重构文件名称是否重复的判断方式

* refactor: the usage of file path descriptor

* fix: remove blank line

* feat: add more test case

* fix: file base name in FilePathDescriptor

* Revert "refactor: the usage of file path descriptor"

This reverts commit b46ff3b4
pull/1509/head
guqing 2021-10-29 19:36:37 +08:00 committed by GitHub
parent 44d740b760
commit 1d91450505
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 498 additions and 212 deletions

View File

@ -18,8 +18,8 @@ import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.properties.AliOssProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.ImageUtils;
/**
@ -27,6 +27,7 @@ import run.halo.app.utils.ImageUtils;
*
* @author MyFaith
* @author ryanwang
* @author guqing
* @date 2019-04-04
*/
@Slf4j
@ -34,9 +35,12 @@ import run.halo.app.utils.ImageUtils;
public class AliOssFileHandler implements FileHandler {
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
public AliOssFileHandler(OptionService optionService) {
public AliOssFileHandler(OptionService optionService,
AttachmentRepository attachmentRepository) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
}
@Override
@ -79,30 +83,21 @@ public class AliOssFileHandler implements FileHandler {
}
try {
final String basename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
final String extension = FilenameUtils.getExtension(file.getOriginalFilename());
final String timestamp = String.valueOf(System.currentTimeMillis());
final StringBuilder upFilePath = new StringBuilder();
if (StringUtils.isNotEmpty(source)) {
upFilePath.append(source)
.append(URL_SEPARATOR);
}
upFilePath.append(basename)
.append("_")
.append(timestamp)
.append(".")
.append(extension);
String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
FilePathDescriptor uploadFilePath = new FilePathDescriptor.Builder()
.setBasePath(basePath.toString())
.setSubPath(source)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.ALIOSS) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
log.info(basePath.toString());
// Upload
final PutObjectResult putObjectResult = ossClient.putObject(bucketName,
upFilePath.toString(),
uploadFilePath.getRelativePath(),
file.getInputStream());
if (putObjectResult == null) {
@ -111,21 +106,22 @@ public class AliOssFileHandler implements FileHandler {
// Response result
final UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
uploadResult.setFilename(uploadFilePath.getName());
String fullPath = uploadFilePath.getFullPath();
uploadResult
.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
uploadResult.setKey(upFilePath.toString());
.setFilePath(StringUtils.isBlank(styleRule) ? fullPath : fullPath + styleRule);
uploadResult.setKey(uploadFilePath.getRelativePath());
uploadResult
.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
uploadResult.setSuffix(uploadFilePath.getExtension());
uploadResult.setSize(file.getSize());
handleImageMetadata(file, uploadResult, () -> {
if (ImageUtils.EXTENSION_ICO.equals(extension)) {
return filePath;
if (ImageUtils.EXTENSION_ICO.equals(uploadFilePath.getExtension())) {
return fullPath;
} else {
return StringUtils.isBlank(thumbnailStyleRule) ? filePath :
filePath + thumbnailStyleRule;
return StringUtils.isBlank(thumbnailStyleRule) ? fullPath :
fullPath + thumbnailStyleRule;
}
});

View File

@ -1,6 +1,5 @@
package run.halo.app.handler.file;
import com.baidubce.auth.DefaultBceCredentials;
import com.baidubce.services.bos.BosClient;
import com.baidubce.services.bos.BosClientConfiguration;
@ -16,8 +15,8 @@ import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.properties.BaiduBosProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.ImageUtils;
/**
@ -32,9 +31,12 @@ import run.halo.app.utils.ImageUtils;
public class BaiduBosFileHandler implements FileHandler {
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
public BaiduBosFileHandler(OptionService optionService) {
public BaiduBosFileHandler(OptionService optionService,
AttachmentRepository attachmentRepository) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
}
@Override
@ -69,40 +71,42 @@ public class BaiduBosFileHandler implements FileHandler {
domain = protocol + domain;
try {
String basename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String timestamp = String.valueOf(System.currentTimeMillis());
String upFilePath = StringUtils.join(basename, "_", timestamp, ".", extension);
String filePath = StringUtils.join(
StringUtils.appendIfMissing(StringUtils.isNotBlank(domain) ? domain : source, "/"),
upFilePath);
FilePathDescriptor pathDescriptor = new FilePathDescriptor.Builder()
.setBasePath(domain)
.setSubPath(source)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.BAIDUBOS) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
// Upload
PutObjectResponse putObjectResponseFromInputStream =
client.putObject(bucketName, upFilePath, file.getInputStream());
client.putObject(bucketName, pathDescriptor.getFullName(), file.getInputStream());
if (putObjectResponseFromInputStream == null) {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到百度云失败 ");
}
// Response result
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
uploadResult.setFilename(pathDescriptor.getFullName());
String fullPath = pathDescriptor.getFullPath();
uploadResult
.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
uploadResult.setKey(upFilePath);
.setFilePath(StringUtils.isBlank(styleRule) ? fullPath : fullPath + styleRule);
uploadResult.setKey(pathDescriptor.getRelativePath());
uploadResult
.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
uploadResult.setSuffix(pathDescriptor.getExtension());
uploadResult.setSize(file.getSize());
// Handle thumbnail
handleImageMetadata(file, uploadResult, () -> {
if (ImageUtils.EXTENSION_ICO.equals(extension)) {
return filePath;
if (ImageUtils.EXTENSION_ICO.equals(pathDescriptor.getExtension())) {
return fullPath;
} else {
return StringUtils.isBlank(thumbnailStyleRule) ? filePath :
filePath + thumbnailStyleRule;
return StringUtils.isBlank(thumbnailStyleRule) ? fullPath :
fullPath + thumbnailStyleRule;
}
});

View File

@ -0,0 +1,189 @@
package run.halo.app.handler.file;
import java.util.function.Predicate;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import run.halo.app.utils.FilenameUtils;
/**
* File path descriptor.
*
* @author guqing
* @since 2021-10-21
*/
@Slf4j
@Data
@Accessors(chain = true)
public final class FilePathDescriptor {
private String name;
private String extension;
private String relativePath;
private String basePath;
private String fullName;
private String fullPath;
private String subPath;
public static final class Builder {
private String name;
private String extension;
private String subPath;
private String basePath;
private String nameSuffix = StringUtils.EMPTY;
private String separator = "/";
private boolean automaticRename;
private Predicate<String> renamePredicate;
private String relativePath;
public Builder setSubPath(String subPath) {
this.subPath = StringUtils.removeEnd(subPath, separator);
return this;
}
public Builder setAutomaticRename(Boolean automaticRename) {
this.automaticRename = automaticRename != null && automaticRename;
return this;
}
public Builder setRenamePredicate(Predicate<String> predicate) {
this.renamePredicate = predicate;
return this;
}
/**
* Set path separator, <code>NULL</code> value is not allowed.
*
* @param separator path separator
* @return builder
*/
public Builder setSeparator(String separator) {
if (separator == null) {
throw new IllegalArgumentException("The separator must not be null.");
}
this.separator = separator;
return this;
}
/**
* Set original file name.
*
* @param originalFileName original file name
* @return file path builder
*/
public Builder setOriginalName(String originalFileName) {
Assert.notNull(originalFileName, "The originalFileName must not be null.");
this.name = FilenameUtils.getBasename(originalFileName);
this.extension = FilenameUtils.getExtension(originalFileName);
return this;
}
public Builder setBasePath(String basePath) {
this.basePath = StringUtils.removeEnd(basePath, separator);
return this;
}
/**
* Set file base name suffix.
*
* @param nameSuffix file base name suffix
* @return builder
*/
public Builder setNameSuffix(String nameSuffix) {
if (nameSuffix == null) {
nameSuffix = StringUtils.EMPTY;
}
this.nameSuffix = nameSuffix;
return this;
}
String buildName() {
StringBuilder sb = new StringBuilder()
.append(this.name);
if (shouldRename()) {
String timestamp = String.valueOf(System.currentTimeMillis());
sb.append('-').append(timestamp);
}
sb.append(this.nameSuffix);
return sb.toString();
}
String getFullName() {
// eg. hello.jpg -> hello-uuid-thumbnail.jpg
if (StringUtils.isNotBlank(this.extension)) {
return this.name + '.' + this.extension;
}
return this.name;
}
String getFullPath() {
if (StringUtils.isNotBlank(this.basePath)) {
return getPath(this.basePath, this.subPath, this.getFullName());
}
return getPath(this.subPath, this.getFullName());
}
String getRelativePath() {
return getPath(this.subPath, getFullName());
}
private boolean shouldRename() {
if (!automaticRename) {
return false;
}
// automaticRename is true
if (renamePredicate == null) {
return true;
}
// renamePredicate not null
return renamePredicate.test(this.relativePath);
}
private String getPath(String first, String... more) {
String path;
if (more.length == 0) {
path = first;
} else {
StringBuilder sb = new StringBuilder();
sb.append(first);
for (String segment : more) {
if (StringUtils.isNotBlank(segment)) {
if (sb.length() > 0) {
sb.append(separator);
}
sb.append(segment);
}
}
path = sb.toString();
}
return path;
}
/**
* build file path object.
*
* @return file path
*/
public FilePathDescriptor build() {
// build relative path first, used to determine if it needs to be renamed
this.relativePath = getRelativePath();
// then build name, returns a new name if the relative path exists
this.name = buildName();
FilePathDescriptor descriptor = new FilePathDescriptor()
.setBasePath(this.basePath)
.setSubPath(this.subPath)
// regenerate relative path
.setRelativePath(getRelativePath())
.setName(this.name)
.setExtension(extension)
.setFullPath(getFullPath())
.setFullName(getFullName());
log.info("FilePathDescriptor: [{}]", descriptor);
return descriptor;
}
}
}

View File

@ -17,14 +17,15 @@ import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.properties.HuaweiObsProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.ImageUtils;
/**
* Huawei obs file handler.
*
* @author qilin
* @author guqing
* @date 2020-04-03
*/
@Slf4j
@ -32,9 +33,12 @@ import run.halo.app.utils.ImageUtils;
public class HuaweiObsFileHandler implements FileHandler {
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
public HuaweiObsFileHandler(OptionService optionService) {
public HuaweiObsFileHandler(OptionService optionService,
AttachmentRepository attachmentRepository) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
}
@Override
@ -77,51 +81,44 @@ public class HuaweiObsFileHandler implements FileHandler {
}
try {
String basename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String timestamp = String.valueOf(System.currentTimeMillis());
StringBuilder upFilePath = new StringBuilder();
if (StringUtils.isNotEmpty(source)) {
upFilePath.append(source)
.append(URL_SEPARATOR);
}
upFilePath.append(basename)
.append("_")
.append(timestamp)
.append(".")
.append(extension);
String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
FilePathDescriptor pathDescriptor = new FilePathDescriptor.Builder()
.setBasePath(domain)
.setSubPath(source)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.HUAWEIOBS) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
log.info(basePath.toString());
// Upload
PutObjectResult putObjectResult =
obsClient.putObject(bucketName, upFilePath.toString(), file.getInputStream());
obsClient.putObject(bucketName, pathDescriptor.getRelativePath(),
file.getInputStream());
if (putObjectResult == null) {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到华为云失败 ");
}
// Response result
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
uploadResult.setFilename(pathDescriptor.getName());
String fullPath = pathDescriptor.getFullPath();
uploadResult
.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
uploadResult.setKey(upFilePath.toString());
.setFilePath(StringUtils.isBlank(styleRule) ? fullPath : fullPath + styleRule);
uploadResult.setKey(pathDescriptor.getRelativePath());
uploadResult
.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
uploadResult.setSuffix(pathDescriptor.getExtension());
uploadResult.setSize(file.getSize());
handleImageMetadata(file, uploadResult, () -> {
if (ImageUtils.EXTENSION_ICO.equals(extension)) {
return filePath;
if (ImageUtils.EXTENSION_ICO.equals(pathDescriptor.getExtension())) {
return fullPath;
} else {
return StringUtils.isBlank(thumbnailStyleRule) ? filePath :
filePath + thumbnailStyleRule;
return StringUtils.isBlank(thumbnailStyleRule) ? fullPath :
fullPath + thumbnailStyleRule;
}
});

View File

@ -13,6 +13,7 @@ import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
@ -20,9 +21,8 @@ import run.halo.app.config.properties.HaloProperties;
import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.support.UploadResult;
import run.halo.app.service.OptionService;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.ImageUtils;
/**
@ -30,6 +30,7 @@ import run.halo.app.utils.ImageUtils;
*
* @author johnniang
* @author ryanwang
* @author guqing
* @date 2019-03-27
*/
@Slf4j
@ -53,13 +54,13 @@ public class LocalFileHandler implements FileHandler {
*/
private static final int THUMB_HEIGHT = 256;
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
private final String workDir;
public LocalFileHandler(OptionService optionService,
public LocalFileHandler(AttachmentRepository attachmentRepository,
HaloProperties haloProperties) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
// Get work dir
workDir = FileHandler.normalizeDirectory(haloProperties.getWorkDir());
@ -83,57 +84,39 @@ public class LocalFileHandler implements FileHandler {
}
}
@NonNull
@Override
public UploadResult upload(MultipartFile file) {
public UploadResult upload(@NonNull 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;
String monthString = month < 10 ? "0" + month : String.valueOf(month);
// Build directory
String subDir = UPLOAD_SUB_DIR + year + FILE_SEPARATOR + monthString + FILE_SEPARATOR;
String originalBasename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
// Get basename
String basename = originalBasename + '-' + 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 file: [{}]to directory: [{}]", file.getOriginalFilename(),
uploadPath.toString());
FilePathDescriptor uploadFilePath = new FilePathDescriptor.Builder()
.setBasePath(workDir)
.setSubPath(generatePath())
.setSeparator(FILE_SEPARATOR)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.LOCAL) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
log.info("Uploading file: [{}] to directory: [{}]", file.getOriginalFilename(),
uploadFilePath.getRelativePath());
Path localFileFullPath = Paths.get(uploadFilePath.getFullPath());
try {
// TODO Synchronize here
// Create directory
Files.createDirectories(uploadPath.getParent());
Files.createFile(uploadPath);
Files.createDirectories(localFileFullPath.getParent());
Files.createFile(localFileFullPath);
// Upload this file
file.transferTo(uploadPath);
file.transferTo(localFileFullPath);
// Build upload result
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(originalBasename);
uploadResult.setFilePath(subFilePath);
uploadResult.setKey(subFilePath);
uploadResult.setSuffix(extension);
uploadResult.setFilename(uploadFilePath.getName());
uploadResult.setFilePath(uploadFilePath.getRelativePath());
uploadResult.setKey(uploadFilePath.getRelativePath());
uploadResult.setSuffix(uploadFilePath.getExtension());
uploadResult
.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSize(file.getSize());
@ -141,28 +124,35 @@ public class LocalFileHandler implements FileHandler {
// TODO refactor this: if image is svg ext. extension
handleImageMetadata(file, uploadResult, () -> {
// Upload a thumbnail
final String thumbnailBasename = basename + THUMBNAIL_SUFFIX;
final String thumbnailSubFilePath = subDir + thumbnailBasename + '.' + extension;
final Path thumbnailPath = Paths.get(workDir + thumbnailSubFilePath);
FilePathDescriptor thumbnailFilePath = new FilePathDescriptor.Builder()
.setBasePath(workDir)
.setSubPath(uploadFilePath.getSubPath())
.setSeparator(FILE_SEPARATOR)
.setOriginalName(uploadFilePath.getFullName())
.setNameSuffix(THUMBNAIL_SUFFIX)
.build();
final Path thumbnailPath = Paths.get(thumbnailFilePath.getFullPath());
try (InputStream is = file.getInputStream()) {
// Generate thumbnail
BufferedImage originalImage = ImageUtils.getImageFromFile(is, extension);
boolean result = generateThumbnail(originalImage, thumbnailPath, extension);
BufferedImage originalImage =
ImageUtils.getImageFromFile(is, uploadFilePath.getExtension());
boolean result = generateThumbnail(originalImage, thumbnailPath,
uploadFilePath.getExtension());
if (result) {
// Set thumb path
return thumbnailSubFilePath;
return thumbnailFilePath.getRelativePath();
}
} catch (Throwable e) {
log.warn("Failed to open image file.", e);
}
return subFilePath;
return uploadFilePath.getRelativePath();
});
log.info("Uploaded file: [{}] to directory: [{}] successfully",
file.getOriginalFilename(), uploadPath.toString());
file.getOriginalFilename(), uploadFilePath.getFullPath());
return uploadResult;
} catch (IOException e) {
throw new FileOperationException("上传附件失败").setErrorData(uploadPath);
throw new FileOperationException("上传附件失败").setErrorData(uploadFilePath.getFullPath());
}
}
@ -205,6 +195,19 @@ public class LocalFileHandler implements FileHandler {
return AttachmentType.LOCAL;
}
private String generatePath() {
// Get current time
Calendar current = Calendar.getInstance();
// Get month and day of month
int year = current.get(Calendar.YEAR);
int month = current.get(Calendar.MONTH) + 1;
String monthString = month < 10 ? "0" + month : String.valueOf(month);
// Build directory
return UPLOAD_SUB_DIR + year + FILE_SEPARATOR + monthString + FILE_SEPARATOR;
}
private boolean generateThumbnail(BufferedImage originalImage, Path thumbPath,
String extension) {
Assert.notNull(originalImage, "Image must not be null");
@ -215,11 +218,10 @@ public class LocalFileHandler implements FileHandler {
try {
Files.createFile(thumbPath);
// Convert to thumbnail and copy the thumbnail
log.debug("Trying to generate thumbnail: [{}]", thumbPath.toString());
log.debug("Trying to generate thumbnail: [{}]", thumbPath);
Thumbnails.of(originalImage).size(THUMB_WIDTH, THUMB_HEIGHT).keepAspectRatio(true)
.toFile(thumbPath.toFile());
log.info("Generated thumbnail image, and wrote the thumbnail to [{}]",
thumbPath.toString());
log.info("Generated thumbnail image, and wrote the thumbnail to [{}]", thumbPath);
result = true;
} catch (Throwable t) {
// Ignore the error

View File

@ -16,14 +16,14 @@ import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.properties.MinioProperties;
import run.halo.app.model.support.HaloConst;
import run.halo.app.model.support.UploadResult;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
/**
* MinIO file handler.
*
* @author Wh1te
* @author guqing
* @date 2020-10-03
*/
@Slf4j
@ -31,9 +31,12 @@ import run.halo.app.utils.FilenameUtils;
public class MinioFileHandler implements FileHandler {
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
public MinioFileHandler(OptionService optionService) {
public MinioFileHandler(OptionService optionService,
AttachmentRepository attachmentRepository) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
}
@NonNull
@ -62,36 +65,36 @@ public class MinioFileHandler implements FileHandler {
.build();
try {
String basename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String timestamp = String.valueOf(System.currentTimeMillis());
String upFilePath = StringUtils
.join(StringUtils.isNotBlank(source) ? source + HaloConst.URL_SEPARATOR : "",
basename, "_", timestamp, ".", extension);
String filePath =
StringUtils.join(endpoint, bucketName, HaloConst.URL_SEPARATOR, upFilePath);
FilePathDescriptor pathDescriptor = new FilePathDescriptor.Builder()
.setBasePath(endpoint + bucketName)
.setSubPath(source)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.MINIO) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.contentType(file.getContentType())
.bucket(bucketName)
.stream(file.getInputStream(), file.getSize(), -1)
.object(upFilePath)
.object(pathDescriptor.getRelativePath())
.build();
minioClient.ignoreCertCheck();
minioClient.putObject(putObjectArgs);
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
uploadResult.setFilePath(filePath);
uploadResult.setKey(upFilePath);
uploadResult.setFilename(pathDescriptor.getName());
uploadResult.setFilePath(pathDescriptor.getFullPath());
uploadResult.setKey(pathDescriptor.getRelativePath());
uploadResult
.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
uploadResult.setSuffix(pathDescriptor.getExtension());
uploadResult.setSize(file.getSize());
// Handle thumbnail
handleImageMetadata(file, uploadResult, () -> filePath);
handleImageMetadata(file, uploadResult, pathDescriptor::getFullPath);
return uploadResult;
} catch (Exception e) {

View File

@ -30,8 +30,8 @@ import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.properties.QiniuOssProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.ImageUtils;
import run.halo.app.utils.JsonUtils;
@ -40,6 +40,7 @@ import run.halo.app.utils.JsonUtils;
*
* @author johnniang
* @author ryanwang
* @author guqing
* @date 2019-03-27
*/
@Slf4j
@ -47,9 +48,12 @@ import run.halo.app.utils.JsonUtils;
public class QiniuOssFileHandler implements FileHandler {
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
public QiniuOssFileHandler(OptionService optionService) {
public QiniuOssFileHandler(OptionService optionService,
AttachmentRepository attachmentRepository) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
}
@Override
@ -96,20 +100,15 @@ public class QiniuOssFileHandler implements FileHandler {
.append(URL_SEPARATOR);
try {
String basename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String timestamp = String.valueOf(System.currentTimeMillis());
StringBuilder upFilePath = new StringBuilder();
if (StringUtils.isNotEmpty(source)) {
upFilePath.append(source)
.append(URL_SEPARATOR);
}
upFilePath.append(basename)
.append("_")
.append(timestamp)
.append(".")
.append(extension);
FilePathDescriptor pathDescriptor = new FilePathDescriptor.Builder()
.setBasePath(basePath.toString())
.setSubPath(source)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.QINIUOSS) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
// Get file recorder for temp directory
FileRecorder fileRecorder = new FileRecorder(tmpPath.toFile());
@ -117,7 +116,8 @@ public class QiniuOssFileHandler implements FileHandler {
UploadManager uploadManager = new UploadManager(configuration, fileRecorder);
// Put the file
Response response = uploadManager
.put(file.getInputStream(), upFilePath.toString(), uploadToken, null, null);
.put(file.getInputStream(), pathDescriptor.getRelativePath(), uploadToken, null,
null);
if (log.isDebugEnabled()) {
log.debug("Qiniu oss response: [{}]", response.toString());
@ -128,25 +128,26 @@ public class QiniuOssFileHandler implements FileHandler {
PutSet putSet = JsonUtils.jsonToObject(response.bodyString(), PutSet.class);
// Get file full path
String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
String fullPath = pathDescriptor.getFullPath();
// Build upload result
UploadResult result = new UploadResult();
result.setFilename(basename);
result.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
result.setKey(upFilePath.toString());
result.setSuffix(extension);
result.setFilename(pathDescriptor.getName());
result.setFilePath(StringUtils.isBlank(styleRule) ? fullPath : fullPath + styleRule);
result.setKey(pathDescriptor.getRelativePath());
result.setSuffix(pathDescriptor.getExtension());
result.setWidth(putSet.getWidth());
result.setHeight(putSet.getHeight());
result.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
result.setSize(file.getSize());
if (isImageType(file)) {
if (ImageUtils.EXTENSION_ICO.equals(extension)) {
result.setThumbPath(filePath);
if (ImageUtils.EXTENSION_ICO.equals(pathDescriptor.getExtension())) {
result.setThumbPath(fullPath);
} else {
result.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? filePath :
filePath + thumbnailStyleRule);
result.setThumbPath(StringUtils.isBlank(thumbnailStyleRule) ? fullPath :
fullPath + thumbnailStyleRule);
}
}

View File

@ -113,7 +113,7 @@ public class SmmsFileHandler implements FileHandler {
// Check status
if (mapResponseEntity.getStatusCode().isError()) {
log.error("Server response detail: [{}]", mapResponseEntity.toString());
log.error("Server response detail: [{}]", mapResponseEntity);
throw new FileOperationException(
"SM.MS 服务状态异常,状态码: " + mapResponseEntity.getStatusCodeValue());
}

View File

@ -1,6 +1,5 @@
package run.halo.app.handler.file;
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
import com.qcloud.cos.COSClient;
@ -21,8 +20,8 @@ import run.halo.app.exception.FileOperationException;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.properties.TencentCosProperties;
import run.halo.app.model.support.UploadResult;
import run.halo.app.repository.AttachmentRepository;
import run.halo.app.service.OptionService;
import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.ImageUtils;
/**
@ -37,9 +36,12 @@ import run.halo.app.utils.ImageUtils;
public class TencentCosFileHandler implements FileHandler {
private final OptionService optionService;
private final AttachmentRepository attachmentRepository;
public TencentCosFileHandler(OptionService optionService) {
public TencentCosFileHandler(OptionService optionService,
AttachmentRepository attachmentRepository) {
this.optionService = optionService;
this.attachmentRepository = attachmentRepository;
}
@Override
@ -88,24 +90,15 @@ public class TencentCosFileHandler implements FileHandler {
}
try {
String basename =
FilenameUtils.getBasename(Objects.requireNonNull(file.getOriginalFilename()));
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
String timestamp = String.valueOf(System.currentTimeMillis());
StringBuilder upFilePath = new StringBuilder();
if (StringUtils.isNotEmpty(source)) {
upFilePath.append(source)
.append(URL_SEPARATOR);
}
upFilePath.append(basename)
.append("_")
.append(timestamp)
.append(".")
.append(extension);
String filePath = StringUtils.join(basePath.toString(), upFilePath.toString());
FilePathDescriptor pathDescriptor = new FilePathDescriptor.Builder()
.setBasePath(basePath.toString())
.setSubPath(source)
.setAutomaticRename(true)
.setRenamePredicate(relativePath ->
attachmentRepository
.countByFileKeyAndType(relativePath, AttachmentType.TENCENTCOS) > 0)
.setOriginalName(file.getOriginalFilename())
.build();
// Upload
ObjectMetadata objectMetadata = new ObjectMetadata();
@ -114,31 +107,31 @@ public class TencentCosFileHandler implements FileHandler {
// 设置 Content type, 默认是 application/octet-stream
objectMetadata.setContentType(file.getContentType());
PutObjectResult putObjectResponseFromInputStream = cosClient
.putObject(bucketName, upFilePath.toString(), file.getInputStream(),
.putObject(bucketName, pathDescriptor.getRelativePath(), file.getInputStream(),
objectMetadata);
if (putObjectResponseFromInputStream == null) {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到腾讯云失败 ");
}
String fullPath = pathDescriptor.getFullPath();
// Response result
UploadResult uploadResult = new UploadResult();
uploadResult.setFilename(basename);
uploadResult.setFilename(pathDescriptor.getName());
uploadResult
.setFilePath(StringUtils.isBlank(styleRule) ? filePath : filePath + styleRule);
uploadResult.setKey(upFilePath.toString());
.setFilePath(StringUtils.isBlank(styleRule) ? fullPath : fullPath + styleRule);
uploadResult.setKey(pathDescriptor.getRelativePath());
uploadResult
.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSuffix(extension);
uploadResult.setSuffix(pathDescriptor.getExtension());
uploadResult.setSize(file.getSize());
// Handle thumbnail
handleImageMetadata(file, uploadResult, () -> {
if (ImageUtils.EXTENSION_ICO.equals(extension)) {
uploadResult.setThumbPath(filePath);
return filePath;
if (ImageUtils.EXTENSION_ICO.equals(pathDescriptor.getExtension())) {
uploadResult.setThumbPath(fullPath);
return fullPath;
} else {
return StringUtils.isBlank(thumbnailStyleRule) ? filePath :
filePath + thumbnailStyleRule;
return StringUtils.isBlank(thumbnailStyleRule) ? fullPath :
fullPath + thumbnailStyleRule;
}
});
return uploadResult;

View File

@ -107,6 +107,7 @@ public class UpOssFileHandler implements FileHandler {
filePath + thumbnailStyleRule;
}
});
result.close();
return uploadResult;
} catch (Exception e) {
throw new FileOperationException("上传附件 " + file.getOriginalFilename() + " 到又拍云失败", e);

View File

@ -41,4 +41,13 @@ public interface AttachmentRepository
* @return count of the given path
*/
long countByPath(@NonNull String path);
/**
* Counts by attachment file key and type.
*
* @param fileKey attachment file key must not be blank
* @param type attachment type must not be null
* @return count of the given path and type
*/
long countByFileKeyAndType(@NonNull String fileKey, @NonNull AttachmentType type);
}

View File

@ -0,0 +1,90 @@
package run.halo.app.handler.file;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* File path descriptor test case.
*
* @author guqing
* @since 2021-10-21
*/
public class FilePathDescriptorTest {
private FilePathDescriptor.Builder descriptorBuilder;
@BeforeEach
void setUp() {
descriptorBuilder = new FilePathDescriptor.Builder()
.setBasePath("/home/halo")
.setSubPath("2021/10/")
.setSeparator(FILE_SEPARATOR)
.setAutomaticRename(false)
.setRenamePredicate(builder -> true)
.setOriginalName("hello.jpg");
}
@Test
public void build() {
FilePathDescriptor descriptor = descriptorBuilder.build();
assertEquals("/home/halo/2021/10/hello.jpg", descriptor.getFullPath());
assertEquals("2021/10/hello.jpg", descriptor.getRelativePath());
}
@Test
public void autoRename() {
FilePathDescriptor descriptor = descriptorBuilder.setAutomaticRename(true).build();
assertNotEquals("/home/halo/2021/10/hello.jpg", descriptor.getFullPath());
assertNotEquals("2021/10/hello.jpg", descriptor.getRelativePath());
}
@Test
public void autoRenameWithPredicate() {
FilePathDescriptor descriptor1 = descriptorBuilder.setAutomaticRename(true)
.setRenamePredicate(path -> false).build();
assertEquals("/home/halo/2021/10/hello.jpg", descriptor1.getFullPath());
assertEquals("2021/10/hello.jpg", descriptor1.getRelativePath());
FilePathDescriptor descriptor2 = descriptorBuilder.setAutomaticRename(true)
.setRenamePredicate(path -> true).build();
assertNotEquals("/home/halo/2021/10/hello.jpg", descriptor2.getFullPath());
assertNotEquals("2021/10/hello.jpg", descriptor2.getRelativePath());
}
@Test
public void separator() {
FilePathDescriptor descriptor = descriptorBuilder.setSeparator("->").build();
assertEquals("/home/halo->2021/10->hello.jpg", descriptor.getFullPath());
assertEquals("2021/10->hello.jpg", descriptor.getRelativePath());
}
@Test
public void nameSuffix() {
FilePathDescriptor descriptor = descriptorBuilder.setNameSuffix("_thumbnail").build();
assertEquals("/home/halo/2021/10/hello_thumbnail.jpg", descriptor.getFullPath());
assertEquals("hello_thumbnail", descriptor.getName());
assertEquals("hello_thumbnail.jpg", descriptor.getFullName());
}
@Test
public void withoutExtension() {
FilePathDescriptor descriptor = descriptorBuilder.setOriginalName("hello").build();
assertEquals("hello", descriptor.getName());
assertEquals("", descriptor.getExtension());
assertEquals("hello", descriptor.getFullName());
assertEquals("2021/10/hello", descriptor.getRelativePath());
}
@Test
public void otherName() {
FilePathDescriptor descriptor = descriptorBuilder.setOriginalName("1.4.9.png").build();
assertEquals("1.4.9", descriptor.getName());
assertEquals("1.4.9.png", descriptor.getFullName());
assertEquals("/home/halo/2021/10/1.4.9.png", descriptor.getFullPath());
assertEquals("2021/10/1.4.9.png", descriptor.getRelativePath());
}
}

View File

@ -26,6 +26,7 @@ class FilenameUtilsTest {
assertEquals("", FilenameUtils.getBasename("a/b/c/"));
assertEquals("o", FilenameUtils.getBasename("he/ll/o.tar.gz"));
assertEquals("i", FilenameUtils.getBasename("h/i.tar.bz2"));
assertEquals("1.4.9", FilenameUtils.getBasename("1.4.9.png"));
}
// foo.txt --> "txt"