Complete uploadQnYun service

pull/137/head
johnniang 2019-03-27 00:40:40 +08:00
parent e042d83baf
commit 3c56b60b95
11 changed files with 224 additions and 41 deletions

View File

@ -0,0 +1,17 @@
package cc.ryanc.halo.exception;
/**
* File upload exception.
*
* @author johnniang
* @date 3/27/19
*/
public class FileUploadException extends ServiceException {
public FileUploadException(String message) {
super(message);
}
public FileUploadException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -4,7 +4,7 @@ package cc.ryanc.halo.model.enums;
* @author : RYAN0UP
* @date : 2019-03-17
*/
public enum BlogProperties implements PropertyEnum<String> {
public enum BlogProperties implements PropertyEnum {
/**
*
@ -149,13 +149,7 @@ public enum BlogProperties implements PropertyEnum<String> {
/**
*
*/
ATTACH_LOC("attach_loc", String.class),
/**
* Zone.
*/
QINIU_ZONE("qiniu_zone", String.class);
ATTACHMENT_TYPE("attachment_type", String.class);
private String value;

View File

@ -9,7 +9,7 @@ import org.springframework.util.Assert;
* @author johnniang
* @date 3/26/19
*/
public interface PropertyEnum<T> extends ValueEnum<T> {
public interface PropertyEnum extends ValueEnum<String> {
/**
* Get property type.
@ -67,7 +67,26 @@ public interface PropertyEnum<T> extends ValueEnum<T> {
}
// Should never happen
throw new UnsupportedOperationException("Unsupported blog property type:" + type.getName() + " provided");
throw new UnsupportedOperationException("Unsupported convention for blog property type:" + type.getName() + " provided");
}
/**
* Converts to enum.
*
* @param value string value must not be null
* @param type propertye value enum type must not be null
* @param <T> property value enum type
* @return property enum value or null
*/
static <T extends Enum<T>> T convertToEnum(@NonNull String value, @NonNull Class<T> type) {
Assert.hasText(value, "Property value must not be blank");
try {
return Enum.valueOf(type, value);
} catch (Exception e) {
// Ignore this exception
return null;
}
}
/**
@ -87,6 +106,7 @@ public interface PropertyEnum<T> extends ValueEnum<T> {
|| type.isAssignableFrom(Byte.class)
|| type.isAssignableFrom(Double.class)
|| type.isAssignableFrom(Float.class)
|| type.isAssignableFrom(Enum.class)
);
}
}

View File

@ -0,0 +1,41 @@
package cc.ryanc.halo.model.enums;
/**
* Qi niu yun properties.
*
* @author johnniang
* @date 3/26/19
*/
public enum QnYunProperties implements PropertyEnum {
ZONE("qiniu_zone", String.class),
ACCESS_KEY("qiniu_access_key", String.class),
SECRET_KEY("qiniu_secret_key", String.class),
DOMAIN("qiniu_domain", String.class),
BUCKET("qiniu_bucket", String.class),
SMALL_URL("qiniu_small_url", String.class);
private String value;
private Class<?> type;
QnYunProperties(String value, Class<?> type) {
if (!PropertyEnum.isSupportedType(type)) {
throw new IllegalArgumentException("Unsupported blog property type: " + type);
}
this.value = value;
this.type = type;
}
@Override
public String getValue() {
return value;
}
@Override
public Class<?> getType() {
return type;
}
}

View File

@ -13,6 +13,15 @@ import lombok.Data;
@Data
public class QiNiuPutSet {
/**
* hash
*/
public String hash;
/**
*
*/
public String key;
/**
*
*/
@ -21,10 +30,10 @@ public class QiNiuPutSet {
/**
*
*/
private Integer w;
private Integer width;
/**
*
*/
private Integer h;
private Integer height;
}

View File

@ -35,4 +35,22 @@ public interface FileService {
*/
@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);
}

View File

@ -3,7 +3,6 @@ package cc.ryanc.halo.service;
import cc.ryanc.halo.exception.MissingPropertyValueException;
import cc.ryanc.halo.model.dto.OptionOutputDTO;
import cc.ryanc.halo.model.entity.Option;
import cc.ryanc.halo.model.enums.BlogProperties;
import cc.ryanc.halo.model.enums.PropertyEnum;
import cc.ryanc.halo.model.params.OptionParam;
import cc.ryanc.halo.service.base.CrudService;
@ -60,7 +59,7 @@ public interface OptionService extends CrudService<Option, Integer> {
* @param properties blog properties
* @param source source
*/
void saveProperties(@NonNull Map<? extends PropertyEnum<String>, String> properties, String source);
void saveProperties(@NonNull Map<? extends PropertyEnum, String> properties, String source);
/**
* Get all options
@ -110,7 +109,7 @@ public interface OptionService extends CrudService<Option, Integer> {
* @return an option value
*/
@Nullable
String getByPropertyOfNullable(@NonNull PropertyEnum<String> property);
String getByPropertyOfNullable(@NonNull PropertyEnum property);
/**
* Gets option value by blog property.
@ -120,7 +119,7 @@ public interface OptionService extends CrudService<Option, Integer> {
* @throws MissingPropertyValueException throws when property value dismisses
*/
@NonNull
String getByPropertyOfNonNull(@NonNull PropertyEnum<String> property);
String getByPropertyOfNonNull(@NonNull PropertyEnum property);
/**
* Gets option value by blog property.
@ -129,7 +128,7 @@ public interface OptionService extends CrudService<Option, Integer> {
* @return an optional option value
*/
@NonNull
Optional<String> getByProperty(@NonNull PropertyEnum<String> property);
Optional<String> getByProperty(@NonNull PropertyEnum property);
/**
* Gets property value by blog property.
@ -140,7 +139,7 @@ public interface OptionService extends CrudService<Option, Integer> {
* @param <T> property type
* @return property value
*/
<T> T getByProperty(@NonNull PropertyEnum<String> property, @NonNull Class<T> propertyType, T defaultValue);
<T> T getByProperty(@NonNull PropertyEnum property, @NonNull Class<T> propertyType, T defaultValue);
/**
* Gets property value by blog property.
@ -150,7 +149,7 @@ public interface OptionService extends CrudService<Option, Integer> {
* @param <T> property type
* @return property value
*/
<T> Optional<T> getByProperty(@NonNull PropertyEnum<String> property, @NonNull Class<T> propertyType);
<T> Optional<T> getByProperty(@NonNull PropertyEnum property, @NonNull Class<T> propertyType);
/**
* Gets value by key.
@ -195,12 +194,12 @@ public interface OptionService extends CrudService<Option, Integer> {
int getRssPageSize();
/**
* Get quniu zone.
* Get qi niu yun zone.
*
* @return qiniu zone
*/
@NonNull
Zone getQiniuZone();
Zone getQnYunZone();
/**
* Gets locale.

View File

@ -1,12 +1,24 @@
package cc.ryanc.halo.service.impl;
import cc.ryanc.halo.config.properties.HaloProperties;
import cc.ryanc.halo.exception.FileUploadException;
import cc.ryanc.halo.exception.ServiceException;
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.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.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;
@ -26,6 +38,7 @@ 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.
@ -58,10 +71,8 @@ public class FileServiceImpl implements FileService {
/**
* Check work directory.
*
* @throws URISyntaxException throws when work directory is not a uri
*/
private void checkWorkDir() throws URISyntaxException {
private void checkWorkDir() {
// Get work path
Path workPath = Paths.get(workDir);
@ -131,7 +142,7 @@ public class FileServiceImpl implements FileService {
uploadResult.setFilename(basename);
uploadResult.setFilePath(subFilePath);
uploadResult.setSuffix(extension);
uploadResult.setMediaType(MediaType.valueOf(file.getContentType()));
uploadResult.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
uploadResult.setSize(file.getSize());
// Check file type
@ -165,6 +176,82 @@ public class FileServiceImpl implements FileService {
}
}
@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.setThumbPath(StringUtils.isBlank(smallUrl) ? filePath : filePath + smallUrl);
result.setSuffix(FilenameUtils.getExtension(file.getOriginalFilename()));
result.setWidth(putSet.getWidth());
result.setHeight(putSet.getHeight());
result.setMediaType(MediaType.valueOf(Objects.requireNonNull(file.getContentType())));
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");
return null;
}
/**
* Generates thumbnail image.
*

View File

@ -5,6 +5,7 @@ import cc.ryanc.halo.model.dto.OptionOutputDTO;
import cc.ryanc.halo.model.entity.Option;
import cc.ryanc.halo.model.enums.BlogProperties;
import cc.ryanc.halo.model.enums.PropertyEnum;
import cc.ryanc.halo.model.enums.QnYunProperties;
import cc.ryanc.halo.model.params.OptionParam;
import cc.ryanc.halo.repository.OptionRepository;
import cc.ryanc.halo.service.OptionService;
@ -86,8 +87,6 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
return;
}
// (Not recommended) Don't write "this::save" here
// Types of key and value are String
options.forEach((key, value) -> save(key, value, source));
}
@ -102,7 +101,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
}
@Override
public void saveProperties(Map<? extends PropertyEnum<String>, String> properties, String source) {
public void saveProperties(Map<? extends PropertyEnum, String> properties, String source) {
if (CollectionUtils.isEmpty(properties)) {
return;
}
@ -151,33 +150,33 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
}
@Override
public String getByPropertyOfNullable(PropertyEnum<String> property) {
public String getByPropertyOfNullable(PropertyEnum property) {
return getByProperty(property).orElse(null);
}
@Override
public String getByPropertyOfNonNull(PropertyEnum<String> property) {
public String getByPropertyOfNonNull(PropertyEnum property) {
Assert.notNull(property, "Blog property must not be null");
return getByKeyOfNonNull(property.getValue());
}
@Override
public Optional<String> getByProperty(PropertyEnum<String> property) {
public Optional<String> getByProperty(PropertyEnum property) {
Assert.notNull(property, "Blog property must not be null");
return getByKey(property.getValue());
}
@Override
public <T> T getByProperty(PropertyEnum<String> property, Class<T> propertyType, T defaultValue) {
public <T> T getByProperty(PropertyEnum property, Class<T> propertyType, T defaultValue) {
Assert.notNull(property, "Blog property must not be null");
return getByProperty(property, propertyType).orElse(defaultValue);
}
@Override
public <T> Optional<T> getByProperty(PropertyEnum<String> property, Class<T> propertyType) {
public <T> Optional<T> getByProperty(PropertyEnum property, Class<T> propertyType) {
return getByProperty(property).map(propertyValue -> PropertyEnum.convertTo(propertyValue, propertyType));
}
@ -222,8 +221,8 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
}
@Override
public Zone getQiniuZone() {
return getByProperty(BlogProperties.QINIU_ZONE).map(qiniuZone -> {
public Zone getQnYunZone() {
return getByProperty(QnYunProperties.ZONE).map(qiniuZone -> {
Zone zone;
switch (qiniuZone) {
@ -262,5 +261,4 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
}).orElseGet(Locale::getDefault);
}
}

View File

@ -173,7 +173,7 @@ public class InstallController {
properties.put(BlogProperties.NEW_COMMENT_NOTICE, Boolean.FALSE.toString());
properties.put(BlogProperties.COMMENT_PASS_NOTICE, Boolean.FALSE.toString());
properties.put(BlogProperties.COMMENT_REPLY_NOTICE, Boolean.FALSE.toString());
properties.put(BlogProperties.ATTACH_LOC, AttachmentType.SERVER.getValue().toString());
properties.put(BlogProperties.ATTACHMENT_TYPE, AttachmentType.SERVER.getValue().toString());
// Create properties
optionService.saveProperties(properties, "system");
@ -261,7 +261,7 @@ public class InstallController {
//// options.put(BlogProperties.NEW_COMMENT_NOTICE, TrueFalseEnum.FALSE.getDesc());
//// options.put(BlogProperties.COMMENT_PASS_NOTICE, TrueFalseEnum.FALSE.getDesc());
//// options.put(BlogProperties.COMMENT_REPLY_NOTICE, TrueFalseEnum.FALSE.getDesc());
//// options.put(BlogProperties.ATTACH_LOC, AttachLocationEnum.SERVER.getDesc());
//// options.put(BlogProperties.ATTACHMENT_TYPE, AttachLocationEnum.SERVER.getDesc());
//// optionService.saveOptions(options);
//
// //更新日志

View File

@ -1,7 +1,7 @@
package cc.ryanc.halo.service.impl;
import cc.ryanc.halo.model.entity.Option;
import cc.ryanc.halo.model.enums.BlogProperties;
import cc.ryanc.halo.model.enums.QnYunProperties;
import cc.ryanc.halo.repository.OptionRepository;
import com.qiniu.common.Zone;
import org.junit.Test;
@ -73,13 +73,13 @@ public class OptionServiceImplTest {
}
private void getQiniuZoneTest(Zone actualZone, Option option) {
BlogProperties zoneProperty = BlogProperties.QINIU_ZONE;
QnYunProperties zoneProperty = QnYunProperties.ZONE;
// Given
given(optionRepository.findByOptionKey(zoneProperty.getValue())).willReturn(Optional.ofNullable(option));
// When
Zone zone = optionService.getQiniuZone();
Zone zone = optionService.getQnYunZone();
// Then
then(optionRepository).should().findByOptionKey(zoneProperty.getValue());