mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-10-06 06:52:58 +08:00
feat(dev): 新增FTP文件存储功能
- 添加FTP文件存储相关配置项到数据库 - 实现FTP文件上传接口和控制器方法 - 集成FTP文件工具类和相关枚举类型 - 添加FTP文件配置表单页面 - 增加前端FTP文件上传功能和API调用 - 添加FTP文件下载和删除功能 - 配置FTP文件操作所需的依赖包
This commit is contained in:
@@ -209,6 +209,13 @@
|
||||
<version>3.45.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- commons-net -->
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
|
||||
@@ -9,8 +9,15 @@
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
import { convertUrl } from '@/utils/apiAdaptive'
|
||||
import sysConfig from '@/config'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/dev/file/` + url, ...arg)
|
||||
const apiUrl = (url) => {
|
||||
const convertedUrl = convertUrl(url)
|
||||
return convertedUrl.startsWith('/api') ? convertedUrl : '/api' + convertedUrl
|
||||
}
|
||||
/**
|
||||
* 文件
|
||||
*
|
||||
@@ -42,6 +49,10 @@ export default {
|
||||
fileUploadMinioReturnId(data) {
|
||||
return request('uploadMinioReturnId', data)
|
||||
},
|
||||
// FTP文件上传,返回文件id
|
||||
fileUploadFtpReturnId(data) {
|
||||
return request('uploadFtpReturnId', data)
|
||||
},
|
||||
// 本地文件上传,返回文件Url
|
||||
fileUploadLocalReturnUrl(data) {
|
||||
return request('uploadLocalReturnUrl', data)
|
||||
@@ -58,6 +69,10 @@ export default {
|
||||
fileUploadMinioReturnUrl(data) {
|
||||
return request('uploadMinioReturnUrl', data)
|
||||
},
|
||||
// FTP文件上传,返回文件Url
|
||||
fileUploadFtpReturnUrl(data) {
|
||||
return request('uploadFtpReturnUrl', data)
|
||||
},
|
||||
// 获取文件分页列表
|
||||
filePage(data) {
|
||||
return request('page', data, 'get')
|
||||
@@ -72,6 +87,21 @@ export default {
|
||||
responseType: 'blob'
|
||||
})
|
||||
},
|
||||
// 授权下载文件,这里要带上blob类型
|
||||
fileAuthDownload(data) {
|
||||
return request('authDownload', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
},
|
||||
// 获取文件预览地址
|
||||
fileDownloadUrl(record) {
|
||||
if (record?.isDownloadAuth) {
|
||||
const token = tool.data.get('TOKEN')
|
||||
const tokenValue = token ? encodeURIComponent(sysConfig.TOKEN_PREFIX + token) : ''
|
||||
return apiUrl(`/dev/file/authDownload?id=${record.id}&${sysConfig.TOKEN_NAME}=${tokenValue}`)
|
||||
}
|
||||
return apiUrl(`/dev/file/download?id=${record.id}`)
|
||||
},
|
||||
// 获取文件详情
|
||||
fileDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
>
|
||||
<a-row :gutter="8">
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="FTP主机:" name="SNOWY_FILE_FTP_HOST">
|
||||
<a-input v-model:value="formData.SNOWY_FILE_FTP_HOST" placeholder="请输入FTP主机" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="FTP端口:" name="SNOWY_FILE_FTP_PORT">
|
||||
<a-input v-model:value="formData.SNOWY_FILE_FTP_PORT" placeholder="请输入FTP端口" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="FTP用户名:" name="SNOWY_FILE_FTP_USERNAME">
|
||||
<a-input v-model:value="formData.SNOWY_FILE_FTP_USERNAME" placeholder="请输入FTP用户名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="FTP密码:" name="SNOWY_FILE_FTP_PASSWORD">
|
||||
<a-input-password v-model:value="formData.SNOWY_FILE_FTP_PASSWORD" placeholder="请输入FTP密码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="FTP基础目录:" name="SNOWY_FILE_FTP_BASE_PATH">
|
||||
<a-input v-model:value="formData.SNOWY_FILE_FTP_BASE_PATH" placeholder="请输入FTP基础目录" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="FTP储存桶:" name="SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME">
|
||||
<a-input v-model:value="formData.SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME" placeholder="请输入FTP储存桶" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="ftpFileForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
|
||||
const param = {
|
||||
category: 'FILE_FTP'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = item.configValue
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
SNOWY_FILE_FTP_HOST: [required('请输入FTP主机')],
|
||||
SNOWY_FILE_FTP_PORT: [required('请输入FTP端口')],
|
||||
SNOWY_FILE_FTP_USERNAME: [required('请输入FTP用户名')],
|
||||
SNOWY_FILE_FTP_PASSWORD: [required('请输入FTP密码')],
|
||||
SNOWY_FILE_FTP_BASE_PATH: [required('请输入FTP基础目录')],
|
||||
SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME: [required('请输入FTP储存桶')]
|
||||
}
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 24
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -15,6 +15,9 @@
|
||||
<a-tab-pane key="rustfsFile" tab="RUSTFS文件">
|
||||
<rustfsFileForm />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="ftpFile" tab="FTP文件">
|
||||
<ftpFileForm />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
@@ -24,5 +27,6 @@
|
||||
import TencentFileForm from './tencentFileForm.vue'
|
||||
import MinioFileForm from './minioFileForm.vue'
|
||||
import RustfsFileForm from './rustfsFileForm.vue'
|
||||
import FtpFileForm from './ftpFileForm.vue'
|
||||
const activeKey = ref('localFile')
|
||||
</script>
|
||||
|
||||
@@ -52,6 +52,17 @@
|
||||
</a-upload-dragger>
|
||||
</a-spin>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="Ftp" tab="FTP">
|
||||
<a-spin :spinning="uploadLoading">
|
||||
<a-upload-dragger :custom-request="customRequestFtp" :show-upload-list="false">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<inbox-outlined></inbox-outlined>
|
||||
</p>
|
||||
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||
<p class="ant-upload-hint">支持单个上传</p>
|
||||
</a-upload-dragger>
|
||||
</a-spin>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</xn-form-container>
|
||||
</template>
|
||||
@@ -130,6 +141,20 @@
|
||||
uploadLoading.value = false
|
||||
})
|
||||
}
|
||||
// 上传FTP文件
|
||||
const customRequestFtp = (data) => {
|
||||
uploadLoading.value = true
|
||||
const fileData = new FormData()
|
||||
fileData.append('file', data.file)
|
||||
fileApi
|
||||
.fileUploadFtpReturnUrl(fileData)
|
||||
.then(() => {
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
uploadLoading.value = false
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
openUpload
|
||||
|
||||
+20
@@ -122,6 +122,26 @@ public interface DevFileApi {
|
||||
**/
|
||||
String storageFileWithReturnIdMinio(MultipartFile file);
|
||||
|
||||
/* =========FTP文件========= */
|
||||
|
||||
/**
|
||||
* 上传文件返回Url
|
||||
*
|
||||
* @param file 文件
|
||||
* @author lilang
|
||||
* @date 2026/06/30
|
||||
**/
|
||||
String storageFileWithReturnUrlFtp(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传文件返回Id
|
||||
*
|
||||
* @param file 文件
|
||||
* @author lilang
|
||||
* @date 2026/06/30
|
||||
**/
|
||||
String storageFileWithReturnIdFtp(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 通过文件id查询文件详情
|
||||
*
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
<artifactId>s3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FTP文件操作依赖,Hutool FTP基于Apache Commons Net -->
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- websocket -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
+26
@@ -249,6 +249,32 @@ public class DevFileController {
|
||||
return CommonResult.data(devFileService.uploadReturnUrl(DevFileEngineTypeEnum.MINIO.getValue(), file));
|
||||
}
|
||||
|
||||
/**
|
||||
* FTP文件上传,返回文件id
|
||||
*
|
||||
* @author lilang
|
||||
* @date 2026/06/30
|
||||
**/
|
||||
@Operation(summary = "上传FTP文件返回id")
|
||||
@CommonLog("上传FTP文件返回id")
|
||||
@PostMapping("/dev/file/uploadFtpReturnId")
|
||||
public CommonResult<String> uploadFtpReturnId(@RequestPart("file") MultipartFile file) {
|
||||
return CommonResult.data(devFileService.uploadReturnId(DevFileEngineTypeEnum.FTP.getValue(), file));
|
||||
}
|
||||
|
||||
/**
|
||||
* FTP文件上传,返回文件Url
|
||||
*
|
||||
* @author lilang
|
||||
* @date 2026/06/30
|
||||
**/
|
||||
@Operation(summary = "上传FTP文件返回url")
|
||||
@CommonLog("上传FTP文件返回url")
|
||||
@PostMapping("/dev/file/uploadFtpReturnUrl")
|
||||
public CommonResult<String> uploadFtpReturnUrl(@RequestPart("file") MultipartFile file) {
|
||||
return CommonResult.data(devFileService.uploadReturnUrl(DevFileEngineTypeEnum.FTP.getValue(), file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件分页列表
|
||||
*
|
||||
|
||||
+4
-1
@@ -36,7 +36,10 @@ public enum DevFileEngineTypeEnum {
|
||||
MINIO("MINIO"),
|
||||
|
||||
/** RUSTFS */
|
||||
RUSTFS("RUSTFS");
|
||||
RUSTFS("RUSTFS"),
|
||||
|
||||
/** FTP */
|
||||
FTP("FTP");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
||||
+10
@@ -96,6 +96,16 @@ public class DevFileApiProvider implements DevFileApi {
|
||||
return devFileService.uploadReturnId(DevFileEngineTypeEnum.MINIO.getValue(), file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String storageFileWithReturnUrlFtp(MultipartFile file) {
|
||||
return devFileService.uploadReturnUrl(DevFileEngineTypeEnum.FTP.getValue(), file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String storageFileWithReturnIdFtp(MultipartFile file) {
|
||||
return devFileService.uploadReturnId(DevFileEngineTypeEnum.FTP.getValue(), file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getFileInfoById(String id) {
|
||||
return Optional.ofNullable(devFileService.getById(id))
|
||||
|
||||
+9
@@ -160,6 +160,8 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
|
||||
return;
|
||||
}
|
||||
CommonDownloadUtil.download(devFile.getName(), IoUtil.readBytes(FileUtil.getInputStream(file)), response);
|
||||
} else if(devFile.getEngine().equals(DevFileEngineTypeEnum.FTP.getValue())) {
|
||||
CommonDownloadUtil.download(devFile.getName(), DevFileFtpUtil.getFileBytes(devFile.getBucket(), devFile.getFileKey()), response);
|
||||
} else {
|
||||
String storagePath = devFile.getStoragePath();
|
||||
CommonDownloadUtil.download(devFile.getName(), HttpUtil.downloadBytes(storagePath), response);
|
||||
@@ -193,6 +195,8 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
|
||||
DevFileMinIoUtil.deleteFile(bucketName, fileKey);
|
||||
} else if (DevFileEngineTypeEnum.RUSTFS.getValue().equals(engine)) {
|
||||
DevFileRustFsUtil.deleteFile(bucketName, fileKey);
|
||||
} else if (DevFileEngineTypeEnum.FTP.getValue().equals(engine)) {
|
||||
DevFileFtpUtil.deleteFile(bucketName, fileKey);
|
||||
} else {
|
||||
log.error("未知存储引擎:{}", engine);
|
||||
}
|
||||
@@ -253,6 +257,11 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
|
||||
// 使用RUSTFS默认配置的bucketName
|
||||
bucketName = DevFileRustFsUtil.getDefaultBucketName();
|
||||
storageUrl = DevFileRustFsUtil.storageFileWithReturnUrl(bucketName, fileKey, file);
|
||||
} else if(engine.equals(DevFileEngineTypeEnum.FTP.getValue())) {
|
||||
|
||||
// 使用FTP默认配置的bucketName
|
||||
bucketName = DevFileFtpUtil.getDefaultBucketName();
|
||||
storageUrl = DevFileFtpUtil.storageFileWithReturnUrl(bucketName, fileKey, file);
|
||||
} else {
|
||||
throw new CommonException("不支持的文件引擎:{}", engine);
|
||||
}
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.dev.modular.file.util;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.ftp.Ftp;
|
||||
import cn.hutool.extra.ftp.FtpMode;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import vip.xiaonuo.common.exception.CommonException;
|
||||
import vip.xiaonuo.dev.api.DevConfigApi;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* FTP文件工具类
|
||||
*
|
||||
* @author lilang
|
||||
* @date 2026/06/30
|
||||
*/
|
||||
public class DevFileFtpUtil {
|
||||
|
||||
private static final String SNOWY_FILE_FTP_HOST_KEY = "SNOWY_FILE_FTP_HOST";
|
||||
private static final String SNOWY_FILE_FTP_PORT_KEY = "SNOWY_FILE_FTP_PORT";
|
||||
private static final String SNOWY_FILE_FTP_USERNAME_KEY = "SNOWY_FILE_FTP_USERNAME";
|
||||
private static final String SNOWY_FILE_FTP_PASSWORD_KEY = "SNOWY_FILE_FTP_PASSWORD";
|
||||
private static final String SNOWY_FILE_FTP_BASE_PATH_KEY = "SNOWY_FILE_FTP_BASE_PATH";
|
||||
private static final String SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME_KEY = "SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME";
|
||||
|
||||
private static Ftp initClient() {
|
||||
DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);
|
||||
String host = devConfigApi.getValueByKey(SNOWY_FILE_FTP_HOST_KEY);
|
||||
if (ObjectUtil.isEmpty(host)) {
|
||||
throw new CommonException("FTP文件操作客户端未正确配置:host为空");
|
||||
}
|
||||
String portStr = devConfigApi.getValueByKey(SNOWY_FILE_FTP_PORT_KEY);
|
||||
if (ObjectUtil.isEmpty(portStr)) {
|
||||
throw new CommonException("FTP文件操作客户端未正确配置:port为空");
|
||||
}
|
||||
Integer port;
|
||||
try {
|
||||
port = Integer.valueOf(portStr);
|
||||
} catch (Exception e) {
|
||||
throw new CommonException("FTP文件操作客户端未正确配置:port格式错误");
|
||||
}
|
||||
String username = devConfigApi.getValueByKey(SNOWY_FILE_FTP_USERNAME_KEY);
|
||||
if (ObjectUtil.isEmpty(username)) {
|
||||
throw new CommonException("FTP文件操作客户端未正确配置:username为空");
|
||||
}
|
||||
String password = devConfigApi.getValueByKey(SNOWY_FILE_FTP_PASSWORD_KEY);
|
||||
if (ObjectUtil.isEmpty(password)) {
|
||||
throw new CommonException("FTP文件操作客户端未正确配置:password为空");
|
||||
}
|
||||
try {
|
||||
return new Ftp(host, port, username, password, StandardCharsets.UTF_8, null, null, FtpMode.Passive);
|
||||
} catch (Exception e) {
|
||||
throw new CommonException("FTP文件操作客户端连接失败:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDefaultBucketName() {
|
||||
DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);
|
||||
String defaultBucketName = devConfigApi.getValueByKey(SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME_KEY);
|
||||
if (ObjectUtil.isEmpty(defaultBucketName)) {
|
||||
throw new CommonException("FTP文件操作客户端未正确配置:defaultBucketName为空");
|
||||
}
|
||||
return defaultBucketName;
|
||||
}
|
||||
|
||||
public static void storageFile(String bucketName, String key, MultipartFile multipartFile) {
|
||||
try (InputStream inputStream = multipartFile.getInputStream()) {
|
||||
storageFile(bucketName, key, inputStream);
|
||||
} catch (IOException e) {
|
||||
throw new CommonException("获取文件流异常,名称是:{}", multipartFile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void storageFile(String bucketName, String key, InputStream inputStream) {
|
||||
Ftp ftp = initClient();
|
||||
try {
|
||||
String dir = getRemoteDir(bucketName, key);
|
||||
String fileName = StrUtil.subAfter(key, StrUtil.SLASH, true);
|
||||
boolean success = ftp.upload(dir, fileName, inputStream);
|
||||
if (!success) {
|
||||
throw new CommonException("FTP文件上传失败:{}", key);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(ftp);
|
||||
}
|
||||
}
|
||||
|
||||
public static String storageFileWithReturnUrl(String bucketName, String key, MultipartFile multipartFile) {
|
||||
storageFile(bucketName, key, multipartFile);
|
||||
return getFileAuthUrl(bucketName, key);
|
||||
}
|
||||
|
||||
public static byte[] getFileBytes(String bucketName, String key) {
|
||||
Ftp ftp = initClient();
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
String dir = getRemoteDir(bucketName, key);
|
||||
String fileName = StrUtil.subAfter(key, StrUtil.SLASH, true);
|
||||
ftp.download(dir, fileName, outputStream);
|
||||
return outputStream.toByteArray();
|
||||
} catch (Exception e) {
|
||||
throw new CommonException("FTP文件下载失败:{}", e.getMessage());
|
||||
} finally {
|
||||
IoUtil.close(ftp);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFileAuthUrl(String bucketName, String key) {
|
||||
return "ftp://" + getHost() + normalizePath(StrUtil.SLASH + getBasePath() + StrUtil.SLASH + bucketName + StrUtil.SLASH + key);
|
||||
}
|
||||
|
||||
public static void deleteFile(String bucketName, String key) {
|
||||
Ftp ftp = initClient();
|
||||
try {
|
||||
String path = normalizePath(getBasePath() + StrUtil.SLASH + bucketName + StrUtil.SLASH + key);
|
||||
if (!ftp.delFile(path)) {
|
||||
throw new CommonException("FTP文件删除失败:{}", path);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(ftp);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRemoteDir(String bucketName, String key) {
|
||||
String dir = StrUtil.subBefore(key, StrUtil.SLASH, true);
|
||||
return normalizePath(getBasePath() + StrUtil.SLASH + bucketName + StrUtil.SLASH + dir);
|
||||
}
|
||||
|
||||
private static String getHost() {
|
||||
DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);
|
||||
return devConfigApi.getValueByKey(SNOWY_FILE_FTP_HOST_KEY);
|
||||
}
|
||||
|
||||
private static String getBasePath() {
|
||||
DevConfigApi devConfigApi = SpringUtil.getBean(DevConfigApi.class);
|
||||
return ObjectUtil.defaultIfEmpty(devConfigApi.getValueByKey(SNOWY_FILE_FTP_BASE_PATH_KEY), StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
private static String normalizePath(String path) {
|
||||
String normalized = path.replace("\\", StrUtil.SLASH);
|
||||
while (normalized.contains("//")) {
|
||||
normalized = normalized.replace("//", StrUtil.SLASH);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
INSERT INTO `DEV_CONFIG` VALUES ('1908870094824755321', 'SNOWY_FILE_FTP_HOST', '127.0.0.1', 'FILE_FTP', 'FTP文件主机', 189, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `DEV_CONFIG` VALUES ('1908870094824755322', 'SNOWY_FILE_FTP_PORT', '21', 'FILE_FTP', 'FTP文件端口', 190, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `DEV_CONFIG` VALUES ('1908870094824755323', 'SNOWY_FILE_FTP_USERNAME', 'ftpuser', 'FILE_FTP', 'FTP文件用户名', 191, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `DEV_CONFIG` VALUES ('1908870094824755324', 'SNOWY_FILE_FTP_PASSWORD', 'ftppassword', 'FILE_FTP', 'FTP文件密码', 192, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `DEV_CONFIG` VALUES ('1908870094824755325', 'SNOWY_FILE_FTP_BASE_PATH', '/snowy', 'FILE_FTP', 'FTP文件基础目录', 193, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `DEV_CONFIG` VALUES ('1908870094824755326', 'SNOWY_FILE_FTP_DEFAULT_BUCKET_NAME', 'defaultbucket', 'FILE_FTP', 'FTP文件默认存储桶', 194, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
|
||||
INSERT INTO `DEV_DICT` VALUES ('1908870094824755331', '1554726376265744386', 'FTP', 'FTP', 'blue', 'FTPStorage', 'FRM', 50, NULL, 'NOT_DELETE', NULL, NULL, NULL, NULL);
|
||||
Reference in New Issue
Block a user