对象存储功能

This commit is contained in:
TinyAnts 2022-03-31 11:45:06 +08:00
parent d3687ed741
commit 447dbcb88b
19 changed files with 416 additions and 68 deletions

View File

@ -1,6 +1,8 @@
package com.hxkj.admin.config;
import com.hxkj.admin.LikeAdminInterceptor;
import com.hxkj.common.config.GlobalConfig;
import com.hxkj.common.utils.YmlUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -40,8 +42,13 @@ public class WebMvcConfig implements WebMvcConfigurer {
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/uploads/**")
.addResourceLocations("file:" + "");
String directory = YmlUtil.get("like.upload-directory");
if (directory == null || directory.equals("")) {
directory = GlobalConfig.uploadDirectory;
}
registry.addResourceHandler("/"+ GlobalConfig.publicPrefix +"/**")
.addResourceLocations("file:" + directory);
}
}

View File

@ -1,18 +1,97 @@
package com.hxkj.admin.controller;
import com.hxkj.admin.LikeAdminThreadLocal;
import com.hxkj.admin.service.IAlbumService;
import com.hxkj.common.core.AjaxResult;
import com.hxkj.common.enums.AlbumEnum;
import com.hxkj.common.exception.OperateException;
import com.hxkj.common.plugin.storage.StorageDriver;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/upload")
public class UploadController {
public Object image() {
return null;
@Resource
IAlbumService iAlbumService;
/**
* 上传图片
*
* @author fzr
* @param request 请求 对象
* @return Object
*/
@PostMapping("/image")
public Object image(HttpServletRequest request) {
MultipartFile multipartFile = ((MultipartRequest) request).getFile("file");
if (multipartFile == null) {
return AjaxResult.failed("请选择上传图片");
}
try {
StorageDriver storageDriver = new StorageDriver();
Map<String, Object> map = storageDriver.upload(multipartFile, "image", AlbumEnum.IMAGE.getCode());
Map<String, String> album = new LinkedHashMap<>();
album.put("aid", String.valueOf(LikeAdminThreadLocal.getAdminId()));
album.put("cid", request.getParameter("cid"));
album.put("type", String.valueOf(AlbumEnum.IMAGE.getCode()));
album.put("size", map.get("size").toString());
album.put("ext", map.get("ext").toString());
album.put("url", map.get("url").toString());
Integer id = iAlbumService.albumAdd(album);
map.put("id", id);
return AjaxResult.success(map);
} catch (OperateException e) {
return AjaxResult.failed(e.getMsg());
}
}
public Object video() {
return null;
/**
* 上传视频
*
* @author fzr
* @param request 请求对象
* @return Object
*/
@PostMapping("/video")
public Object video(HttpServletRequest request) {
MultipartFile multipartFile = ((MultipartRequest) request).getFile("file");
if (multipartFile == null) {
return AjaxResult.failed("请选择上传视频");
}
try {
StorageDriver storageDriver = new StorageDriver();
Map<String, Object> map = storageDriver.upload(multipartFile, "video", AlbumEnum.IMAGE.getCode());
Map<String, String> album = new LinkedHashMap<>();
album.put("cid", request.getParameter("cid"));
album.put("aid", String.valueOf(LikeAdminThreadLocal.getAdminId()));
album.put("type", String.valueOf(AlbumEnum.Video.getCode()));
album.put("ext", map.get("ext").toString());
album.put("size", map.get("size").toString());
album.put("url", map.get("url").toString());
Integer id = iAlbumService.albumAdd(album);
map.put("id", id);
return AjaxResult.success(map);
} catch (OperateException e) {
return AjaxResult.failed(e.getMsg());
}
}
}

View File

@ -48,7 +48,7 @@ public interface IAlbumService extends BaseService<Album> {
* @author fzr
* @param params 文件信息参数
*/
void albumAdd(Map<String, String> params);
Integer albumAdd(Map<String, String> params);
/**
* 文件删除

View File

@ -136,18 +136,20 @@ public class IAlbumServiceImpl extends MPJBaseServiceImpl<AlbumMapper, Album> im
* @param params 文件信息参数
*/
@Override
public void albumAdd(Map<String, String> params) {
public Integer albumAdd(Map<String, String> params) {
Album album = new Album();
album.setCid(Integer.parseInt(params.getOrDefault("cid", "0")));
album.setAid(Integer.parseInt(params.getOrDefault("aid", "0")));
album.setUid(Integer.parseInt(params.getOrDefault("uid", "0")));
album.setType(Integer.parseInt(params.get("type")));
album.setName(params.get("name"));
album.setExt(params.get("ext"));
album.setUri(params.get("uri"));
album.setUri(params.get("url"));
album.setSize(Long.parseLong(params.get("size")));
album.setCreateTime(System.currentTimeMillis() / 1000);
album.setUpdateTime(System.currentTimeMillis() / 1000);
this.save(album);
return album.getId();
}
/**

View File

@ -0,0 +1,14 @@
{
"properties": [
{
"name": "like.debug",
"type": "java.lang.String",
"description": "Description for like.debug."
},
{
"name": "like.upload-directory",
"type": "java.lang.String",
"description": "Description for like.upload-directory."
}
]
}

View File

@ -1,3 +1,8 @@
# 项目配置
like:
debug: true
upload-directory: D:\www\frame\
# 服务配置
server:
port: 8082

View File

@ -87,11 +87,16 @@
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- 七牛云OSS -->
<!-- 七牛云 -->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<!-- 腾讯云COS -->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
</dependency>
<!-- 阿里云OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>

View File

@ -20,4 +20,19 @@ public class GlobalConfig {
// 资源访问前缀
public static String publicPrefix = "uploads";
// 上传映射目录
public static String uploadDirectory = "/www/wwwroot/uploads/";
// 上传图片限制
public static Integer uploadImageSize = 1024 * 1024 * 10;
// 上传视频限制
public static Integer uploadVideoSize = 1024 * 1024 * 10;
// 上传图片扩展
public static String[] uploadImageExt = new String[] {"png", "jpg", "jpeg", "gif", "ico", "bmp"};
// 上传视频扩展
public static String[] uploadVideoExt = new String[] {"mp4", "mp3", "avi", "flv", "rmvb", "mov"};
}

View File

@ -0,0 +1,41 @@
package com.hxkj.common.enums;
/**
* 相册枚举
*/
public enum AlbumEnum {
IMAGE(10, "图片"),
Video(20, "视频");
/**
* 构造方法
*/
private final int code;
private final String msg;
AlbumEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 获取状态码
*
* @author fzr
* @return Long
*/
public int getCode() {
return this.code;
}
/**
* 获取提示
*
* @author fzr
* @return String
*/
public String getMsg() {
return this.msg;
}
}

View File

@ -16,7 +16,6 @@ public enum HttpEnum {
TOKEN_INVALID(333, "token参数无效"),
NO_PERMISSION(403, "无相关权限"),
NOT_DATA(404, "无相关数据"),
SYSTEM_ERROR(500, "系统错误");

View File

@ -27,9 +27,10 @@ public class GlobalException {
* 处理所有不可知异常
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(Throwable.class)
public AjaxResult handleException(Throwable e) {
log.error(e.getMessage());
@ExceptionHandler(Exception.class)
@ResponseBody
public AjaxResult handleException(Exception e) {
System.out.println(e.getMessage());
return AjaxResult.failed(HttpEnum.SYSTEM_ERROR.getCode(), HttpEnum.SYSTEM_ERROR.getMsg());
}

View File

@ -1,14 +1,18 @@
package com.hxkj.common.plugin.storage;
import com.hxkj.common.config.GlobalConfig;
import com.hxkj.common.exception.OperateException;
import com.hxkj.common.plugin.storage.engine.Aliyun;
import com.hxkj.common.plugin.storage.engine.Local;
import com.hxkj.common.plugin.storage.engine.Qcloud;
import com.hxkj.common.plugin.storage.engine.Qiniu;
import com.hxkj.common.utils.ConfigUtil;
import com.hxkj.common.utils.TimeUtil;
import com.hxkj.common.utils.ToolsUtil;
import com.hxkj.common.utils.UrlUtil;
import org.springframework.web.multipart.MultipartFile;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
public class StorageDriver {
@ -35,17 +39,43 @@ public class StorageDriver {
*
* @author fzr
* @param multipartFile 文件对象
* @param folder 文件夹
* @param type 类型: 10=图片, 20=视频
*/
public void upload(MultipartFile multipartFile) {
String key = this.buildSaveName(multipartFile);
public Map<String, Object> upload(MultipartFile multipartFile, String folder, Integer type) {
this.checkFile(multipartFile, type);
String key = this.buildSaveName(multipartFile);
switch (this.engine) {
case "local":
Local local = new Local();
local.upload(multipartFile, key, folder);
break;
case "qiniu":
Qiniu qiniu = new Qiniu(this.config);
qiniu.upload(multipartFile, key);
qiniu.upload(multipartFile, folder + "/" + key);
break;
case "aliyun":
Aliyun aliyun = new Aliyun(this.config);
aliyun.upload(multipartFile, folder + "/" + key);
break;
case "qcloud":
Qcloud qcloud = new Qcloud(this.config);
qcloud.upload(multipartFile, folder + "/" + key);
break;
}
String origFileName = Objects.requireNonNull(multipartFile.getOriginalFilename());
String origFileExt = origFileName.substring(origFileName.lastIndexOf(".")).replace(".", "");
String newFileName = folder + "/" + key;
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", 0);
map.put("name", multipartFile.getOriginalFilename());
map.put("size", multipartFile.getSize());
map.put("ext", origFileExt.toLowerCase());
map.put("url", newFileName);
map.put("path", UrlUtil.toAbsoluteUrl(newFileName));
return map;
}
/**
@ -58,8 +88,37 @@ public class StorageDriver {
private String buildSaveName(MultipartFile multipartFile) {
String name = multipartFile.getOriginalFilename();
String ext = Objects.requireNonNull(name).substring(name.lastIndexOf("."));
String date = TimeUtil.timestampToDate(TimeUtil.timestamp(), "Ymd");
String date = TimeUtil.timestampToDate(TimeUtil.timestamp(), "yyyyMMdd");
return date + "/" + UUID.randomUUID() + ext.toLowerCase();
}
/**
* 文件验证
*
* @author fzr
* @param multipartFile 文件对象
* @param type 类型: 10=图片, 20=视频
*/
private void checkFile(MultipartFile multipartFile, Integer type) {
String fileName = Objects.requireNonNull(multipartFile.getOriginalFilename());
String fileExt = fileName.substring(fileName.lastIndexOf(".")).replace(".", "").toLowerCase();
long fileSize = multipartFile.getSize();
if (type == 10) {
if (!Arrays.asList(GlobalConfig.uploadImageExt).contains(fileExt)) {
throw new OperateException("不被支持的扩展:" + fileExt);
}
if (fileSize > GlobalConfig.uploadImageSize) {
throw new OperateException("上传图片不能超出限制:" + GlobalConfig.uploadImageSize);
}
} else if (type == 20) {
if (!Arrays.asList(GlobalConfig.uploadVideoExt).contains(fileExt)) {
throw new OperateException("不被支持的扩展:" + fileExt);
}
if (fileSize > GlobalConfig.uploadVideoSize) {
throw new OperateException("上传视频不能超出限制:" + GlobalConfig.uploadImageSize);
}
}
}
}

View File

@ -5,31 +5,61 @@ import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import com.hxkj.common.exception.OperateException;
import com.qiniu.util.Auth;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Map;
public class Aliyun {
public void upload() {
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
String accessKeyId = "LTAI4G9XZP9MKQ2AmeZJGRTE";
String accessKeySecret = "qbA9DoJ41jnmpoKhiZqmU1dfHFcpvN";
/**
* 存储配置
*/
private final Map<String, String> config;
String bucketName = "yixiangonline";
String objectName = "exampleobject.txt";
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
/**
* 构造方法
*/
public Aliyun(Map<String, String> config) {
this.config = config;
}
/**
* 鉴权令牌
*
* @author fzr
* @return String
*/
public OSS ossClient() {
String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
String accessKeyId = this.config.get("access_key");
String accessKeySecret = this.config.get("secret_key");
return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
/**
* 上传文件
*
* @param multipartFile 文件对象
* @param key 文件名称 20220331/11.png
*/
public void upload(MultipartFile multipartFile, String key) {
try {
String content = "Hello OSS";
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));
ossClient.putObject(putObjectRequest);
PutObjectRequest putObjectRequest = new PutObjectRequest(
this.config.get("bucket"), key,
new ByteArrayInputStream(multipartFile.getBytes())
);
this.ossClient().putObject(putObjectRequest);
} catch (OSSException oe) {
System.out.println("Error Message:" + oe.getErrorMessage());
} catch (ClientException ce) {
System.out.println("Error Message:" + ce.getMessage());
throw new OperateException(oe.getMessage());
} catch (Exception ce) {
throw new OperateException(ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
if (this.ossClient() != null) {
this.ossClient().shutdown();
}
}
}

View File

@ -1,36 +1,36 @@
package com.hxkj.common.plugin.storage.engine;
import com.hxkj.common.config.GlobalConfig;
import com.hxkj.common.exception.OperateException;
import com.hxkj.common.utils.YmlUtil;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;
public class Local {
@Resource
HttpServletRequest request;
public void upload() {
MultipartFile multipartFile = ((MultipartRequest) request).getFile("file");
if (multipartFile == null) {
throw new OperateException("请选择要上传的图片");
/**
* 本地上传
*
* @author fzr
* @param multipartFile 上传对象
* @param key 文件名称 20220331/122.jpg
* @param folder 文件夹
*/
public void upload(MultipartFile multipartFile, String key, String folder) {
// 映射目录
String directory = YmlUtil.get("like.upload-directory");
if (directory == null || directory.equals("")) {
directory = GlobalConfig.uploadDirectory;
}
// 文件信息
String origFileName = multipartFile.getOriginalFilename();
String origFileExt = Objects.requireNonNull(origFileName).substring(origFileName.lastIndexOf("."));
long origFileSize = multipartFile.getSize();
String newsFileName = UUID.randomUUID() + origFileExt;
String newsSavePath = "";
String saveName = key.split("/")[1];
String savePath = (directory + folder + "/" + key.split("/")[0]).replace("\\", "/");
// 创建目录
File fileExist = new File(newsSavePath);
File fileExist = new File(savePath);
if (!fileExist.exists()) {
if (!fileExist.mkdirs()) {
throw new OperateException("创建上传目录失败");
@ -39,9 +39,9 @@ public class Local {
// 保存文件
try {
File dest = new File(newsSavePath, newsFileName);
File dest = new File(savePath, saveName);
multipartFile.transferTo(dest);
} catch (IOException e) {
} catch (Exception e) {
throw new OperateException("上传文件失败:"+e.getMessage());
}

View File

@ -1,4 +1,72 @@
package com.hxkj.common.plugin.storage.engine;
import com.hxkj.common.exception.OperateException;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.region.Region;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.Map;
public class Qcloud {
/**
* 存储配置
*/
private final Map<String, String> config;
/**
* 构造方法
*/
public Qcloud(Map<String, String> config) {
this.config = config;
}
/**
* 鉴权客户端
*
* @author fzr
* @return String
*/
public COSClient cosClient() {
String secretId = this.config.get("access_key");
String secretKey = this.config.get("secret_key");
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
Region region = new Region(this.config.get("region"));
ClientConfig clientConfig = new ClientConfig(region);
clientConfig.setHttpProtocol(HttpProtocol.https);
return new COSClient(cred, clientConfig);
}
/**
* 上传文件
*
* @param multipartFile 文件对象
* @param key 文件名称 20220331/11.png
*/
public void upload(MultipartFile multipartFile, String key) {
try {
byte[] data = multipartFile.getBytes();
InputStream inputStream = new ByteArrayInputStream(data);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
PutObjectRequest putObjectRequest = new PutObjectRequest(this.config.get("bucket"), key, inputStream, objectMetadata);
this.cosClient().putObject(putObjectRequest);
} catch (CosClientException e) {
e.printStackTrace();
} catch (Exception e) {
throw new OperateException(e.getMessage());
}
}
}

View File

@ -2,8 +2,6 @@ package com.hxkj.common.plugin.storage.engine;
import com.google.gson.Gson;
import com.hxkj.common.exception.OperateException;
import com.hxkj.common.utils.TimeUtil;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
@ -14,8 +12,6 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public class Qiniu {

View File

@ -97,6 +97,10 @@ public class ConfigUtil {
.eq("type", type)
.eq("name", name));
if (config.getValue().equals("") || config.getValue().equals("[]")) {
return new LinkedHashMap<>();
}
return ToolsUtil.jsonToStrMap(config.getValue());
}

View File

@ -1,5 +1,9 @@
package com.hxkj.common.utils;
import com.hxkj.common.config.GlobalConfig;
import java.util.Map;
/**
* 文件路径处理工具
*/
@ -8,7 +12,7 @@ public class UrlUtil {
/**
* 访问前缀
*/
private static final String uploadPrefix = "upload";
private static final String uploadPrefix = GlobalConfig.publicPrefix;
/**
* 转绝对路径
@ -18,11 +22,17 @@ public class UrlUtil {
* @return String
*/
public static String toAbsoluteUrl(String url) {
if(url.indexOf("/")!=0) {
if(url.indexOf("/") != 0) {
url = "/" + url;
}
return HttpUtil.domain() + "/" + uploadPrefix + url;
String engine = ConfigUtil.get("storage", "default", "local");
if (engine.equals("local")) {
return HttpUtil.domain() + "/" + uploadPrefix + url;
}
Map<String, String> config = ConfigUtil.getMap("storage", engine);
return config.get("domain") + url;
}
/**
@ -37,7 +47,13 @@ public class UrlUtil {
return "";
}
return url.replace(HttpUtil.domain() + "/" + uploadPrefix + "/", "");
String engine = ConfigUtil.get("storage", "default", "local");
if (engine.equals("local")) {
return url.replace(HttpUtil.domain() + "/" + uploadPrefix + "/", "");
}
Map<String, String> config = ConfigUtil.getMap("storage", engine);
return url.replace(config.get("domain") + "/" + uploadPrefix + "/", "");
}
}

13
pom.xml
View File

@ -30,7 +30,8 @@
<fastJson.version>1.2.78</fastJson.version>
<apache-commons.version>2.10.0</apache-commons.version>
<gson.version>2.9.0</gson.version>
<qiniu-oss.version>7.9.5</qiniu-oss.version>
<qiniu.version>7.9.5</qiniu.version>
<qcloud-version>5.6.54</qcloud-version>
<aliyun-oss.version>3.10.2</aliyun-oss.version>
</properties>
@ -93,11 +94,17 @@
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- 七牛云OSS -->
<!-- 七牛云 -->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>${qiniu-oss.version}</version>
<version>${qiniu.version}</version>
</dependency>
<!-- 腾讯云COS -->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>${qcloud-version}</version>
</dependency>
<!-- 阿里云OSS -->
<dependency>