From cad22fbcfda6d7623c00c4d824825de53a275452 Mon Sep 17 00:00:00 2001 From: fengheliang Date: Wed, 31 Jul 2024 15:24:15 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0go-fastdfs=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...30\345\202\250\345\271\263\345\217\260.md" | 2 + ...70\350\247\201\351\227\256\351\242\230.md" | 6 + ...53\351\200\237\345\205\245\351\227\250.md" | 19 + pom.xml | 8 + x-file-storage-core/pom.xml | 13 + .../storage/core/FileStorageProperties.java | 45 ++ .../core/FileStorageServiceBuilder.java | 16 + .../core/platform/GoFastDfsFileStorage.java | 576 ++++++++++++++++++ .../spring/SpringFileStorageProperties.java | 18 + .../src/main/resources/application.yml | 11 +- 10 files changed, 713 insertions(+), 1 deletion(-) create mode 100644 x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/platform/GoFastDfsFileStorage.java diff --git "a/docs/\345\255\230\345\202\250\345\271\263\345\217\260.md" "b/docs/\345\255\230\345\202\250\345\271\263\345\217\260.md" index f52fb72..3e0a693 100644 --- "a/docs/\345\255\230\345\202\250\345\271\263\345\217\260.md" +++ "b/docs/\345\255\230\345\202\250\345\271\263\345\217\260.md" @@ -26,6 +26,8 @@ | FastDFS | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | [查看](存储平台?id=OCI_FastDFS) | | Azure Blob Storage | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | [查看](存储平台?id=OCI_AzureBlobStorage) | | Mongo GridFS | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | ✔️ | [查看](存储平台?id=OCI_MongoGridFS) | +| go-fastdfs | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | | + 对于兼容 Amazon S3 的存储平台,直接将配置写在 Amazon S3 中即可,具体兼容性见下图。 diff --git "a/docs/\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/docs/\345\270\270\350\247\201\351\227\256\351\242\230.md" index 3d01548..dde9293 100644 --- "a/docs/\345\270\270\350\247\201\351\227\256\351\242\230.md" +++ "b/docs/\345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -189,3 +189,9 @@ FileStorageProperties properties = fileStorageService.getProperties(); ### FastDFS 文件名、路径等不生效,缩略图等问题 参考 [兼容性说明-FastDFS](存储平台?id=OCI_FastDFS) + +--------------- + +### go-fastdfs 文件删除异常等问题 +1. 删除问题时提示 “Can only be called by the cluster ip or 127.0.0.1 or admin_ips(cfg.json)”,需要调整go-fastdfs的配置文件,将 `admin_ips` 设置为 `["*"]`,然后重启 go-fastdfs +2. 如果使用数据库存储file_detail,按照url删除文件时,提示“TooManyResultsException”,需要调整go-fastdfs的配置文件,将 `enable_distinct_file` 设置为 `false`,然后重启 go-fastdfs diff --git "a/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" index 4f4f95b..9ca1e66 100644 --- "a/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" +++ "b/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" @@ -222,6 +222,25 @@ ``` +#### **go-fastdfs** + +注意go-fastdfs删除、详情等功能需要开启服务端的白名单后才能使用 + +```yaml +goFastDfs: + - platform: gofastdfs-1 # 存储平台标识 + enable-storage: true # 启用存储 + server: # 服务端地址 例如http://172.26.11.44:10081 + group: 'group1' # 服务端组名 + scene: 'scene' #上传场景 + time-out: 30000 #访问服务端超时时间 + domain: '' # 访问域名,注意“/”结尾,例如:https://file.abc.com/ + ramdom: true + base-path: '' # 基础路径 +``` + +更多参数请参考 `org.dromara.x.file.storage.spring.SpringFileStorageProperties.SpringGoFastDfsConfig` + #### **本地** 无需依赖 diff --git a/pom.xml b/pom.xml index 71832a1..404e683 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,14 @@ Contrib + + fenghlkevin + fenghlkevin + fenghlkevin@gmail.com + + Contrib + + diff --git a/x-file-storage-core/pom.xml b/x-file-storage-core/pom.xml index f97461a..7f1abcc 100644 --- a/x-file-storage-core/pom.xml +++ b/x-file-storage-core/pom.xml @@ -167,6 +167,19 @@ provided true + + + cn.hutool + hutool-http + provided + true + + + cn.hutool + hutool-json + provided + true + org.apache.tika diff --git a/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageProperties.java b/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageProperties.java index 0d9feaa..2c82f4c 100644 --- a/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageProperties.java +++ b/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageProperties.java @@ -140,6 +140,11 @@ public class FileStorageProperties { */ private List mongoGridFs = new ArrayList<>(); + /** + * GoFastDFS + */ + private List goFastdfs = new ArrayList<>(); + /** * 基本的存储平台配置 */ @@ -1056,6 +1061,46 @@ public class FileStorageProperties { private Map attr = new LinkedHashMap<>(); } + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class GoFastDfsConfig extends BaseConfig { + + /** + * http://172.24.5.163:8080 + */ + private String server; + + /** + * 服务器组名 + */ + private String group; + + /** + * 服务器场景 + */ + private String scene; + + /** + * 超时时间 + */ + private Integer timeOut; + + /** + * domain + */ + private String domain; + + /** + * 上传时候base路径 + */ + private String basePath; + /** + * 其它自定义配置 + */ + private Map attr = new LinkedHashMap<>(); + } + /** * 通用的 Client 对象池配置,详情见 {@link org.apache.commons.pool2.impl.GenericObjectPoolConfig} */ diff --git a/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageServiceBuilder.java b/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageServiceBuilder.java index 3ea9d45..586c940 100644 --- a/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageServiceBuilder.java +++ b/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/FileStorageServiceBuilder.java @@ -249,6 +249,8 @@ public class FileStorageServiceBuilder { fileStorageList.addAll(buildFastDfsFileStorage(properties.getFastdfs(), clientFactoryList)); fileStorageList.addAll(buildAzureBlobFileStorage(properties.getAzureBlob(), clientFactoryList)); fileStorageList.addAll(buildMongoGridFsStorage(properties.getMongoGridFs(), clientFactoryList)); + fileStorageList.addAll(buildGoFastDfsStorage(properties.getGoFastdfs())); + // 本体 FileStorageService service = new FileStorageService(); @@ -588,6 +590,20 @@ public class FileStorageServiceBuilder { .collect(Collectors.toList()); } + /** + * 根据配置文件创建goFastDfs存储平台 + */ + public static List buildGoFastDfsStorage(List list) { + if (CollUtil.isEmpty(list)) return Collections.emptyList(); + return list.stream() + .map(config -> { + log.info("加载GoFastDfs存储平台:{}", config.getPlatform()); + return new GoFastDfsFileStorage(config); + }) + .collect(Collectors.toList()); + } + + /** * 获取或创建指定存储平台的 Client 工厂对象 */ diff --git a/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/platform/GoFastDfsFileStorage.java b/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/platform/GoFastDfsFileStorage.java new file mode 100644 index 0000000..c4599b8 --- /dev/null +++ b/x-file-storage-core/src/main/java/org/dromara/x/file/storage/core/platform/GoFastDfsFileStorage.java @@ -0,0 +1,576 @@ +package org.dromara.x.file.storage.core.platform; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.io.resource.InputStreamResource; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageProperties; +import org.dromara.x.file.storage.core.UploadPretreatment; +import org.dromara.x.file.storage.core.constant.Constant; +import org.dromara.x.file.storage.core.exception.ExceptionFactory; +import org.dromara.x.file.storage.core.get.*; +import org.dromara.x.file.storage.core.hash.HashInfo; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * @author fengheliang + */ +@Slf4j +public class GoFastDfsFileStorage implements FileStorage { + + /** + * 上传文件服务器 + */ + private static final String URL_FILE_UPLOAD = "{}/{}/upload"; + + /** + * 删除文件服务器文件 + */ + private static final String URL_FILE_DELETE = "{}/{}/delete"; + + /** + * 获取文件信息 + */ + private static final String URL_GET_FILE_INFO = "{}/{}/get_file_info"; + + /** + * 获取文件列表 + */ + private static final String URL_LIST_DIR = "{}/{}/list_dir?dir={}"; + + private String platform; + + /** + * 服务器地址 + */ + private String server; + + /** + * 服务器组名,用于拼接url + */ + private String group; + + /** + * 服务器场景 + */ + private String scene; + + /** + * 超时时间 + */ + private Integer timeOut; + + private String domain; + + private String basePath; + + private Map attr; + + /** + * 请求静态变量 path + */ + private static final String PARAM_PATH = "path"; + + /** + * 请求静态变量 md5 + */ + private static final String PARAM_MD5 = "md5"; + + /** + * 请求静态变量 scene + */ + private static final String PARAM_SCENE = "scene"; + + /** + * 请求静态变量 output + */ + private static final String PARAM_OUTPUT = "output"; + + /** + * 请求静态变量 filename + */ + private static final String PARAM_FILENAME = "filename"; + + /** + * 请求静态变量 file + */ + private static final String PARAM_FILE = "file"; + + /** + * 请求静态变量 json + */ + private static final String PARAM_RESULT_JSON = "json"; + + /** + * 请求静态变量 status + */ + private static final String PARAM_RESULT_STATUS = "status"; + + /** + * 请求静态变量 message + */ + private static final String PARAM_RESULT_MESSAGE = "message"; + + /** + * 请求静态变量 fail + */ + private static final String PARAM_RESULT_FAIL = "fail"; + + /** + * 请求静态变量 data + */ + private static final String PARAM_RESULT_DATA = "data"; + + /** + * + */ + private static final String PARAM_RESULT_PERMISSIONS = "Can only be called by the cluster ip or 127.0.0.1"; + + public GoFastDfsFileStorage(FileStorageProperties.GoFastDfsConfig goFastDfsConfig) { + this.platform = goFastDfsConfig.getPlatform(); + this.server = goFastDfsConfig.getServer(); + this.group = goFastDfsConfig.getGroup(); + this.scene = goFastDfsConfig.getScene(); + this.timeOut = goFastDfsConfig.getTimeOut(); + this.domain = goFastDfsConfig.getDomain(); + this.attr = goFastDfsConfig.getAttr(); + this.basePath = goFastDfsConfig.getBasePath(); + } + + @Override + public String getPlatform() { + return this.platform; + } + + @Override + public void setPlatform(String platform) { + this.platform = platform; + } + + /** + * 上传文件 + * + * @param inputStream + * @param fileName + * @param pre + * @return + * @throws IOException + */ + private UploadFileResult uploadFile(InputStream inputStream, String fileName, UploadPretreatment pre) { + FileInfo fileInfo = new FileInfo(); + fileInfo.setFilename(fileName); + InputStreamResource inputStreamResource = new InputStreamResource(inputStream, fileName); + + Map params = new HashMap<>(10); + params.put(PARAM_FILE, inputStreamResource); + if (StrUtil.isNotEmpty(basePath)) { + params.put(PARAM_PATH, basePath + "/" + pre.getPath()); + } else { + params.put(PARAM_PATH, pre.getPath()); + } + if (StrUtil.isNotEmpty(fileName)) { + params.put(PARAM_FILENAME, fileName); + } + params.put(PARAM_SCENE, this.scene); + params.put(PARAM_OUTPUT, PARAM_RESULT_JSON); + HttpResponse httpResponse = HttpUtil.createPost(jointPath(URL_FILE_UPLOAD)) + .form(params) + .timeout(this.timeOut) + .execute(); + + if (!httpResponse.isOk()) { + ExceptionFactory.upload(fileInfo, platform, new RuntimeException(httpResponse.body())); + return null; + } + + JSONObject jsonObject = JSONUtil.parseObj(httpResponse.body()); + if (ObjUtil.isNotEmpty(jsonObject.getStr(PARAM_RESULT_STATUS))) { + ExceptionFactory.upload(fileInfo, platform, new RuntimeException(jsonObject.getStr(PARAM_RESULT_MESSAGE))); + return null; + } + + return JSONUtil.toBean(jsonObject, UploadFileResult.class); + } + + private void fillFileInfo(UploadFileResult uploadFileResult, FileInfo fileInfo, UploadPretreatment pre) { + fileInfo.setHashInfo(new HashInfo()); + + String md5 = String.valueOf(uploadFileResult.getMd5()); + pre.getHashCalculatorManager().getHashInfo().put(Constant.Hash.MessageDigest.MD5, uploadFileResult.getMd5()); + fileInfo.getHashInfo().put(Constant.Hash.MessageDigest.MD5, md5); + + String urlPath = StrUtil.isBlank(this.domain) + ? String.valueOf(uploadFileResult.getPath()) + : String.valueOf(uploadFileResult.getPath()).substring(1); + + fileInfo.setUrl(domain + urlPath); + fileInfo.setPath(uploadFileResult.getPath()); + fileInfo.setMetadata(new HashMap<>()); + fileInfo.setUserMetadata(new HashMap<>()); + fileInfo.setSize(uploadFileResult.getSize()); + if (ObjUtil.isEmpty(this.attr)) { + Dict dict = Dict.create(); + dict.putAll(this.attr); + fileInfo.setAttr(dict); + } + } + + private void fillThFileInfo(UploadFileResult uploadFileResult, FileInfo fileInfo, UploadPretreatment pre) { + String urlPath = StrUtil.isBlank(this.domain) + ? uploadFileResult.getPath() + : uploadFileResult.getPath().substring(1); + fileInfo.setThUrl(domain + urlPath); + fileInfo.setThMetadata(new HashMap<>()); + fileInfo.setThUserMetadata(new HashMap<>()); + fileInfo.setThUserMetadata(new HashMap<>()); + fileInfo.setThSize(uploadFileResult.getSize()); + } + + @Override + public boolean save(FileInfo fileInfo, UploadPretreatment pre) { + try { + String fileName = fileInfo.getFilename(); + String thFileName = fileInfo.getThFilename(); + UploadFileResult uploadFileResult = uploadFile(pre.getFileWrapper().getInputStream(), fileName, pre); + + if (ObjUtil.isEmpty(uploadFileResult)) { + return false; + } + + fillFileInfo(uploadFileResult, fileInfo, pre); + + // 上传小图 + if (ObjUtil.isNotEmpty(pre.getThumbnailBytes())) { + UploadFileResult thUploadFileResult = + uploadFile(new ByteArrayInputStream(pre.getThumbnailBytes()), thFileName, pre); + if (ObjUtil.isEmpty(thUploadFileResult)) { + throw new RuntimeException("上传thumbnail文件异常"); + } else { + fillThFileInfo(thUploadFileResult, fileInfo, pre); + } + } + } catch (Exception e) { + ExceptionFactory.upload(fileInfo, platform, e); + return false; + } + return true; + } + + private String jointPath(String methodUrl) { + return this.jointPath(methodUrl, null); + } + + private String jointPath(String methodUrl, String args) { + if (StrUtil.isBlank(args)) { + return StrUtil.format(methodUrl, this.server, this.group); + } else { + return StrUtil.format(methodUrl, this.server, this.group, args); + } + } + + @Override + public boolean delete(FileInfo fileInfo) { + HttpResponse httpResponse = HttpUtil.createPost(jointPath(URL_FILE_DELETE)) + .form(MapUtil.builder(new HashMap(5)) + .put(PARAM_MD5, fileInfo.getHashInfo().getMd5()) + .build()) + .timeout(this.timeOut) + .execute(); + + if (!httpResponse.isOk()) { + ExceptionFactory.delete(fileInfo, platform, new RuntimeException(httpResponse.body())); + return false; + } + + String body = httpResponse.body(); + if (body.startsWith(PARAM_RESULT_PERMISSIONS)) { + ExceptionFactory.delete(fileInfo, platform, new RuntimeException(body)); + return false; + } + + JSONObject jsonObject = JSONUtil.parseObj(body); + + if (ObjUtil.isEmpty(jsonObject.get(PARAM_RESULT_STATUS)) + || PARAM_RESULT_FAIL.equalsIgnoreCase(jsonObject.getStr(PARAM_RESULT_STATUS))) { + ExceptionFactory.delete(fileInfo, platform, new RuntimeException(jsonObject.getStr(PARAM_RESULT_MESSAGE))); + return false; + } + + return true; + } + + private JSONObject getFile(String md5, String path, String fileName, String url) { + + GetFilePretreatment pre = new GetFilePretreatment(); + pre.setPlatform(this.platform); + pre.setPath(path); + pre.setFilename(fileName); + pre.setUrl(url); + + HttpResponse httpResponse = null; + HttpRequest post = HttpUtil.createPost(jointPath(URL_GET_FILE_INFO)); + if (StrUtil.isNotBlank(md5)) { + httpResponse = post.form(MapUtil.builder(new HashMap(5)) + .put(PARAM_MD5, md5) + .build()) + .timeout(this.timeOut) + .execute(); + } else if (StrUtil.isNotBlank(path)) { + httpResponse = post.form(MapUtil.builder(new HashMap(5)) + .put(PARAM_PATH, path) + .build()) + .timeout(this.timeOut) + .execute(); + } else { + ExceptionFactory.getFile(pre, basePath, new RuntimeException("获取文件失败,md5与path同时为空")); + return null; + } + + if (!httpResponse.isOk()) { + ExceptionFactory.getFile(pre, basePath, new RuntimeException(httpResponse.body())); + return null; + } + + JSONObject jsonObject = JSONUtil.parseObj(httpResponse.body()); + if (PARAM_RESULT_FAIL.startsWith(jsonObject.getStr(PARAM_RESULT_STATUS))) { + ExceptionFactory.getFile(pre, basePath, new RuntimeException(jsonObject.getStr(PARAM_RESULT_MESSAGE))); + return null; + } + return jsonObject; + } + + @Override + public boolean exists(FileInfo fileInfo) { + JSONObject file = + getFile(fileInfo.getHashInfo().getMd5(), fileInfo.getPath(), fileInfo.getFilename(), fileInfo.getUrl()); + if (ObjUtil.isEmpty(file)) { + return false; + } + return true; + } + + @Override + public void download(FileInfo fileInfo, Consumer consumer) { + String downloadUrl = this.server + fileInfo.getUrl(); + try { + consumer.accept(getFile(downloadUrl)); + } catch (Exception e) { + ExceptionFactory.download(fileInfo, this.platform, new RuntimeException(e.getMessage())); + } + } + + @Override + public void downloadTh(FileInfo fileInfo, Consumer consumer) { + String downloadUrl = this.server + fileInfo.getThUrl(); + try { + consumer.accept(getFile(downloadUrl)); + } catch (Exception e) { + ExceptionFactory.downloadTh(fileInfo, this.platform, new RuntimeException(e.getMessage())); + } + } + + private InputStream getFile(String fileUrl) { + String url = fileUrl.replaceAll(" ", "%20"); + HttpResponse execute = HttpUtil.createGet(url).execute(); + if (execute.isOk()) { + return execute.bodyStream(); + } else { + throw new RuntimeException("下载文件异常"); + } + } + + @Override + public RemoteFileInfo getFile(GetFilePretreatment pre) { + + JSONObject file = this.getFile(null, pre.getPath(), pre.getFilename(), pre.getUrl()); + if (ObjUtil.isEmpty(file)) { + return null; + } + GoFastFileBean data = JSONUtil.toBean(file.getJSONObject(PARAM_RESULT_DATA), GoFastFileBean.class); + + RemoteFileInfo info = new RemoteFileInfo(); + info.setPlatform(pre.getPlatform()); + info.setBasePath(basePath); + info.setPath(pre.getPath()); + info.setFilename(data.getName()); + info.setUrl(domain + pre.getPath()); + info.setSize(data.getSize()); + info.setExt(FileNameUtil.extName(info.getFilename())); + info.setETag(null); + info.setContentDisposition(null); + info.setContentType(null); + info.setContentMd5(data.getMd5()); + info.setLastModified(DateUtil.date(data.getTimeStamp() * 1000)); + info.setMetadata(MapUtil.newHashMap()); + info.setUserMetadata(MapUtil.newHashMap()); + info.setOriginal(file); + return info; + } + + @Override + public ListFilesSupportInfo isSupportListFiles() { + return ListFilesSupportInfo.supportAll(); + } + + @Override + public ListFilesResult listFiles(ListFilesPretreatment pre) { + HttpResponse httpResponse = HttpUtil.createGet(jointPath(URL_LIST_DIR, pre.getPath())) + .timeout(this.timeOut) + .execute(); + + if (!httpResponse.isOk()) { + ExceptionFactory.listFiles(pre, this.basePath, new RuntimeException(httpResponse.body())); + return null; + } + + String body = httpResponse.body(); + if (body.startsWith(PARAM_RESULT_PERMISSIONS)) { + ExceptionFactory.listFiles(pre, this.basePath, new RuntimeException(body)); + return null; + } + + JSONObject jsonObject = JSONUtil.parseObj(body); + + if (StrUtil.isBlank(jsonObject.getStr(PARAM_RESULT_STATUS))) { + ExceptionFactory.listFiles( + pre, this.basePath, new RuntimeException(jsonObject.getStr(PARAM_RESULT_MESSAGE))); + return null; + } + + ListFilesResult list = new ListFilesResult(); + + List listItemBeans = + JSONUtil.toList(jsonObject.getJSONArray(PARAM_RESULT_DATA), ListItemBean.class); + + List dirList = ListUtil.toList(); + List fileList = ListUtil.toList(); + list.setDirList(dirList); + list.setFileList(fileList); + for (ListItemBean listItemBean : listItemBeans) { + if (listItemBean.isDir) { + RemoteDirInfo dir = new RemoteDirInfo(); + dir.setPlatform(pre.getPlatform()); + dir.setBasePath(basePath); + dir.setPath(pre.getPath()); + dir.setName(listItemBean.getName()); + dirList.add(dir); + } else { + RemoteFileInfo info = new RemoteFileInfo(); + info.setPlatform(pre.getPlatform()); + info.setBasePath(basePath); + info.setPath(pre.getPath()); + info.setFilename(listItemBean.getName()); + info.setSize(listItemBean.getSize()); + info.setExt(FileNameUtil.extName(info.getFilename())); + info.setLastModified(DateUtil.date(listItemBean.getMtime() * 1000)); + fileList.add(info); + } + } + + list.setPlatform(pre.getPlatform()); + list.setBasePath(basePath); + list.setPath(pre.getPath()); + list.setFilenamePrefix(pre.getFilenamePrefix()); + list.setMaxFiles(listItemBeans.size()); + list.setIsTruncated(false); + list.setMarker(null); + list.setNextMarker(null); + + return list; + } + + @Data + public static class GoFastFileBean implements Serializable { + private String md5; + private String name; + private int offset; + private String path; + private List peers; + private String rename; + private String scene; + private long size; + private long timeStamp; + } + + @Data + public static class ListItemBean implements Serializable { + private boolean isDir; + private String md5; + private long mtime; + private String path; + private long size; + private String name; + } + + @Data + public static class UploadFileResult implements Serializable { + /** + * 文件服务器地址 + */ + private String domain; + + /** + * 文件的md5值 + */ + private String md5; + + /** + * 媒体时间 + */ + private String mtime; + + /** + * 文件服务器存放地址 + */ + private String path; + + /** + * 回调结果 + */ + private Long retcode; + + /** + * 回调结果信息 + */ + private String retmsg; + + /** + * 文件服务器的场景 + */ + private String scene; + + /** + * 文件的大小 + */ + private Long size; + + /** + * 文件的源地址 + */ + private String src; + /** + * 文件查看下载地址 + */ + private String url; + } +} diff --git a/x-file-storage-spring/src/main/java/org/dromara/x/file/storage/spring/SpringFileStorageProperties.java b/x-file-storage-spring/src/main/java/org/dromara/x/file/storage/spring/SpringFileStorageProperties.java index b7f0ad1..d81bc58 100644 --- a/x-file-storage-spring/src/main/java/org/dromara/x/file/storage/spring/SpringFileStorageProperties.java +++ b/x-file-storage-spring/src/main/java/org/dromara/x/file/storage/spring/SpringFileStorageProperties.java @@ -153,6 +153,11 @@ public class SpringFileStorageProperties { */ private List mongoGridFs = new ArrayList<>(); + /** + * GoFastDFS + */ + private List goFastdfs = new ArrayList<>(); + /** * 转换成 FileStorageProperties ,并过滤掉没有启用的存储平台 */ @@ -207,6 +212,9 @@ public class SpringFileStorageProperties { properties.setMongoGridFs(mongoGridFs.stream() .filter(SpringMongoGridFsConfig::getEnableStorage) .collect(Collectors.toList())); + properties.setGoFastdfs(goFastdfs.stream() + .filter(SpringGoFastDfsConfig::getEnableStorage) + .collect(Collectors.toList())); return properties; } @@ -450,4 +458,14 @@ public class SpringFileStorageProperties { */ private Boolean enableStorage = false; } + + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringGoFastDfsConfig extends GoFastDfsConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } } diff --git a/x-file-storage-tests/x-file-storage-general-test/src/main/resources/application.yml b/x-file-storage-tests/x-file-storage-general-test/src/main/resources/application.yml index e07373b..e61b754 100644 --- a/x-file-storage-tests/x-file-storage-general-test/src/main/resources/application.yml +++ b/x-file-storage-tests/x-file-storage-general-test/src/main/resources/application.yml @@ -21,7 +21,7 @@ spring: dromara: x-file-storage: #文件存储配置,不使用的情况下可以不写 - default-platform: local-plus-1 #默认使用的存储平台 + default-platform: go-fastdfs-1 #默认使用的存储平台 thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】 local: # 本地存储(不推荐使用) - platform: local-1 # 存储平台标识 @@ -175,3 +175,12 @@ dromara: bucket-name: ccd # GridFS 存储通名称 domain: https://abc.com/ # 访问域名,注意“/”结尾,主要用于配合你自己写的访问接口进行访问 base-path: hy/ # 基础路径 + go-fastdfs: + - platform: go-fastdfs-1 # 存储平台标识 + enable-storage: true # 启用存储 + server: '127.0.0.1:38880' #服务地址 + group: group1 #组名 + scene: x-file-storage #场景 + time-out: 30000 #超时时间 + domain: http://127.0.0.1:38880/ # 访问域名,注意“/”结尾,与 server 保持一致 + base-path: '/abc' # 基础路径 -- Gitee From 5fe6c09205dc529e12927e44f80db174bc554daa Mon Sep 17 00:00:00 2001 From: fengheliang Date: Wed, 31 Jul 2024 15:31:21 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=85=A5=E9=97=A8?= =?UTF-8?q?=E9=87=8C=E9=9D=A2=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" index 9ca1e66..0957bab 100644 --- "a/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" +++ "b/docs/\345\277\253\351\200\237\345\205\245\351\227\250.md" @@ -227,8 +227,8 @@ 注意go-fastdfs删除、详情等功能需要开启服务端的白名单后才能使用 ```yaml -goFastDfs: - - platform: gofastdfs-1 # 存储平台标识 +go-fastdfs: + - platform: go-fastdfs-1 # 存储平台标识 enable-storage: true # 启用存储 server: # 服务端地址 例如http://172.26.11.44:10081 group: 'group1' # 服务端组名 -- Gitee