feat(system): 新增大文件分片上传功能
- 在ISysOssService接口中增加文件流式上传方法,支持避免OOM - 实现SysOssServiceImpl中的文件流式上传逻辑 - 新增BigUploadController控制器处理分片上传、合并、恢复与清理 - 添加ChunkedUploadService服务类管理分片上传业务逻辑 - 创建BigUploadCleanupScheduler定时任务清理过期分片 - 提供FileMultipartFile适配器将File对象转换为MultipartFile
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
package com.ruoyi.web.controller.common;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.AjaxResult;
|
||||||
|
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||||
|
import com.ruoyi.web.service.ChunkedUploadService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/common/bigUpload")
|
||||||
|
public class BigUploadController {
|
||||||
|
|
||||||
|
private final ChunkedUploadService chunkedUploadService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件分片
|
||||||
|
*
|
||||||
|
* @param chunk 文件分片数据
|
||||||
|
* @param chunkIndex 分片索引
|
||||||
|
* @param totalChunks 总分片数
|
||||||
|
* @param fileMd5 文件MD5值
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @return 上传结果
|
||||||
|
*/
|
||||||
|
@PostMapping(value = "/chunk", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
public AjaxResult uploadChunk(@RequestPart("chunk") MultipartFile chunk,
|
||||||
|
@RequestParam("chunkIndex") int chunkIndex,
|
||||||
|
@RequestParam("totalChunks") int totalChunks,
|
||||||
|
@RequestParam("fileMd5") String fileMd5,
|
||||||
|
@RequestParam("fileName") String fileName) {
|
||||||
|
try {
|
||||||
|
chunkedUploadService.saveChunk(fileMd5, fileName, chunkIndex, totalChunks, chunk);
|
||||||
|
return AjaxResult.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并文件分片
|
||||||
|
*
|
||||||
|
* @param fileMd5 文件MD5值
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param totalChunks 总分片数
|
||||||
|
* @return 合并结果及文件信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/merge")
|
||||||
|
public AjaxResult merge(@RequestParam("fileMd5") String fileMd5,
|
||||||
|
@RequestParam("fileName") String fileName,
|
||||||
|
@RequestParam("totalChunks") int totalChunks) {
|
||||||
|
try {
|
||||||
|
SysOssVo oss = chunkedUploadService.mergeChunks(fileMd5, fileName, totalChunks);
|
||||||
|
return AjaxResult.success()
|
||||||
|
.put("url", oss.getUrl())
|
||||||
|
.put("fileName", oss.getOriginalName())
|
||||||
|
.put("ossId", oss.getOssId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 恢复上传(删除最后一个分片)
|
||||||
|
*
|
||||||
|
* @param fileMd5 文件MD5值
|
||||||
|
* @return 操作结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/resume")
|
||||||
|
public AjaxResult resume(@RequestParam("fileMd5") String fileMd5) {
|
||||||
|
try {
|
||||||
|
boolean deleted = chunkedUploadService.deleteLastChunk(fileMd5);
|
||||||
|
return deleted ? AjaxResult.success() : AjaxResult.error("没有可删除的分片");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取已上传的分片列表
|
||||||
|
*
|
||||||
|
* @param fileMd5 文件MD5值
|
||||||
|
* @return 已上传分片索引列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public AjaxResult listUploaded(@RequestParam("fileMd5") String fileMd5) {
|
||||||
|
try {
|
||||||
|
List<Integer> indices = chunkedUploadService.listUploadedChunks(fileMd5);
|
||||||
|
return AjaxResult.success().put("uploaded", indices);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期的临时分片目录
|
||||||
|
*
|
||||||
|
* @param olderThanHours 多少小时未更新则视为过期(默认24小时)
|
||||||
|
*/
|
||||||
|
@PostMapping("/cleanup")
|
||||||
|
public AjaxResult cleanup(@RequestParam(value = "olderThanHours", required = false, defaultValue = "24") int olderThanHours) {
|
||||||
|
long millis = olderThanHours * 60L * 60L * 1000L;
|
||||||
|
int removed = chunkedUploadService.cleanupStaleChunks(millis);
|
||||||
|
return AjaxResult.success().put("removed", removed).put("olderThanHours", olderThanHours);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.web.schedule;
|
||||||
|
|
||||||
|
import com.ruoyi.web.service.ChunkedUploadService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@EnableScheduling
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BigUploadCleanupScheduler {
|
||||||
|
|
||||||
|
private final ChunkedUploadService chunkedUploadService;
|
||||||
|
|
||||||
|
// 每天凌晨2点执行
|
||||||
|
@Scheduled(cron = "0 0 2 * * ?")
|
||||||
|
public void cleanup() {
|
||||||
|
int hours = 24; // 阈值: 24小时未更新
|
||||||
|
long millis = hours * 60L * 60L * 1000L;
|
||||||
|
try {
|
||||||
|
int removed = chunkedUploadService.cleanupStaleChunks(millis);
|
||||||
|
log.info("BigUploadCleanupScheduler removed {} stale chunk directories older than {} hours", removed, hours);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("BigUploadCleanupScheduler cleanup failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
package com.ruoyi.web.service;
|
||||||
|
|
||||||
|
import com.ruoyi.common.config.RuoYiConfig;
|
||||||
|
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||||
|
import com.ruoyi.system.service.ISysOssService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ChunkedUploadService {
|
||||||
|
|
||||||
|
private final ISysOssService ossService;
|
||||||
|
private final ConcurrentMap<String, ReentrantLock> mergeLocks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public ChunkedUploadService(ISysOssService ossService) {
|
||||||
|
this.ossService = ossService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getTempDir(String fileMd5) {
|
||||||
|
String base = RuoYiConfig.getUploadPath();
|
||||||
|
File dir = new File(base, "chunks" + File.separator + fileMd5);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
//无检查结果方法调用忽略
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getFinalDir() {
|
||||||
|
String base = RuoYiConfig.getUploadPath();
|
||||||
|
File dir = new File(base, "big");
|
||||||
|
if (!dir.exists()) {
|
||||||
|
//无检查结果方法调用忽略
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveChunk(String fileMd5, String fileName, int chunkIndex, int totalChunks, MultipartFile chunk) throws IOException {
|
||||||
|
File tempDir = getTempDir(fileMd5);
|
||||||
|
// 为了安全,记录总区块的元数据
|
||||||
|
File meta = new File(tempDir, ".total");
|
||||||
|
if (!meta.exists()) {
|
||||||
|
try (RandomAccessFile raf = new RandomAccessFile(meta, "rw")) {
|
||||||
|
raf.setLength(0);
|
||||||
|
raf.write(String.valueOf(totalChunks).getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File partFile = new File(tempDir, chunkIndex + ".part");
|
||||||
|
chunk.transferTo(partFile);
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
// 更新目录与元数据的修改时间
|
||||||
|
// 无检查结果方法调用忽略
|
||||||
|
partFile.setLastModified(now);
|
||||||
|
meta.setLastModified(now);
|
||||||
|
tempDir.setLastModified(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SysOssVo mergeChunks(String fileMd5, String fileName, int totalChunks) throws Exception {
|
||||||
|
ReentrantLock lock = mergeLocks.computeIfAbsent(fileMd5, k -> new ReentrantLock());
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
File tempDir = getTempDir(fileMd5);
|
||||||
|
if (!tempDir.exists() || !tempDir.isDirectory()) {
|
||||||
|
throw new IOException("临时分片目录不存在");
|
||||||
|
}
|
||||||
|
// 验证
|
||||||
|
List<File> parts = new ArrayList<>();
|
||||||
|
for (int i = 0; i < totalChunks; i++) {
|
||||||
|
File f = new File(tempDir, i + ".part");
|
||||||
|
if (!f.exists()) {
|
||||||
|
throw new IOException("缺少分片: " + i);
|
||||||
|
}
|
||||||
|
parts.add(f);
|
||||||
|
}
|
||||||
|
// 计算大小和偏移量
|
||||||
|
long[] sizes = new long[totalChunks];
|
||||||
|
long[] offsets = new long[totalChunks];
|
||||||
|
long totalSize = 0L;
|
||||||
|
for (int i = 0; i < totalChunks; i++) {
|
||||||
|
sizes[i] = parts.get(i).length();
|
||||||
|
offsets[i] = totalSize;
|
||||||
|
totalSize += sizes[i];
|
||||||
|
}
|
||||||
|
File finalDir = getFinalDir();
|
||||||
|
String safeName = fileMd5 + "_" + (fileName != null ? fileName : "file");
|
||||||
|
File target = new File(finalDir, safeName);
|
||||||
|
if (target.exists()) {
|
||||||
|
// 覆盖现有
|
||||||
|
if (!target.delete()) {
|
||||||
|
throw new IOException("无法覆盖已存在的目标文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 预分配
|
||||||
|
try (RandomAccessFile raf = new RandomAccessFile(target, "rw")) {
|
||||||
|
raf.setLength(totalSize);
|
||||||
|
}
|
||||||
|
int threads = Math.min(8, totalChunks);
|
||||||
|
ExecutorService pool = Executors.newFixedThreadPool(threads);
|
||||||
|
List<Future<?>> futures = new ArrayList<>();
|
||||||
|
for (int i = 0; i < totalChunks; i++) {
|
||||||
|
final int idx = i;
|
||||||
|
futures.add(pool.submit(() -> {
|
||||||
|
try (RandomAccessFile writeRaf = new RandomAccessFile(target, "rw");
|
||||||
|
RandomAccessFile readRaf = new RandomAccessFile(parts.get(idx), "r");
|
||||||
|
FileChannel in = readRaf.getChannel();
|
||||||
|
FileChannel out = writeRaf.getChannel()) {
|
||||||
|
out.position(offsets[idx]);
|
||||||
|
long remaining = sizes[idx];
|
||||||
|
long pos = 0;
|
||||||
|
while (remaining > 0) {
|
||||||
|
long transferred = in.transferTo(pos, remaining, out);
|
||||||
|
if (transferred <= 0) break;
|
||||||
|
pos += transferred;
|
||||||
|
remaining -= transferred;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
pool.shutdown();
|
||||||
|
for (Future<?> f : futures) {
|
||||||
|
f.get();
|
||||||
|
}
|
||||||
|
// 合并成功后的清理部件
|
||||||
|
for (File f : parts) {
|
||||||
|
// 无检查结果方法调用
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
// 无检查结果方法调用
|
||||||
|
new File(tempDir, ".total").delete();
|
||||||
|
// 移除空文件
|
||||||
|
File[] remain = tempDir.listFiles();
|
||||||
|
if (remain == null || remain.length == 0) {
|
||||||
|
// 无检查结果方法调用
|
||||||
|
tempDir.delete();
|
||||||
|
}
|
||||||
|
// 上传合并文件到 OSS 服务和记录(流式,避免OOM)
|
||||||
|
SysOssVo ossVo = ossService.upload(target, fileName, 0L);
|
||||||
|
// 上传完成后删除本地合并的临时文件(可选,节省磁盘)
|
||||||
|
if (target.exists()) {
|
||||||
|
// 无检查结果方法调用
|
||||||
|
target.delete();
|
||||||
|
}
|
||||||
|
return ossVo;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
lock.unlock();
|
||||||
|
} finally {
|
||||||
|
mergeLocks.remove(fileMd5, lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteLastChunk(String fileMd5) {
|
||||||
|
File tempDir = getTempDir(fileMd5);
|
||||||
|
if (!tempDir.exists()) return false;
|
||||||
|
File[] files = tempDir.listFiles((dir, name) -> name.endsWith(".part"));
|
||||||
|
if (files == null || files.length == 0) return false;
|
||||||
|
Arrays.sort(files, Comparator.comparingInt(f -> Integer.parseInt(f.getName().replace(".part", ""))));
|
||||||
|
File last = files[files.length - 1];
|
||||||
|
return last.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> listUploadedChunks(String fileMd5) throws IOException {
|
||||||
|
File tempDir = getTempDir(fileMd5);
|
||||||
|
if (!tempDir.exists()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
File[] parts = tempDir.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.endsWith(".part");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
if (parts != null) {
|
||||||
|
for (File f : parts) {
|
||||||
|
String n = f.getName().replace(".part", "");
|
||||||
|
try {
|
||||||
|
result.add(Integer.parseInt(n));
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.sort(Integer::compareTo);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int cleanupStaleChunks(long olderThanMillis) {
|
||||||
|
int deleted = 0;
|
||||||
|
File base = new File(RuoYiConfig.getUploadPath(), "chunks");
|
||||||
|
if (!base.exists() || !base.isDirectory()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
File[] dirs = base.listFiles(File::isDirectory);
|
||||||
|
if (dirs == null) return 0;
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
for (File dir : dirs) {
|
||||||
|
String key = dir.getName();
|
||||||
|
ReentrantLock lock = mergeLocks.computeIfAbsent(key, k -> new ReentrantLock());
|
||||||
|
if (!lock.tryLock()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
long lastMod = dir.lastModified();
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File f : files) {
|
||||||
|
lastMod = Math.max(lastMod, f.lastModified());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (now - lastMod >= olderThanMillis) {
|
||||||
|
deleteDirectory(dir);
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
lock.unlock();
|
||||||
|
} finally {
|
||||||
|
mergeLocks.remove(key, lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteDirectory(File dir) {
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File f : files) {
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
deleteDirectory(f);
|
||||||
|
} else {
|
||||||
|
// 无检查结果方法调用忽略
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 无检查结果方法调用忽略
|
||||||
|
dir.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.ruoyi.web.service.support;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
public class FileMultipartFile implements MultipartFile {
|
||||||
|
private final File file;
|
||||||
|
private final String originalFilename;
|
||||||
|
private final String contentType;
|
||||||
|
|
||||||
|
public FileMultipartFile(File file, String originalFilename, String contentType) {
|
||||||
|
this.file = file;
|
||||||
|
this.originalFilename = originalFilename;
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return originalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOriginalFilename() {
|
||||||
|
return originalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return file.length() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSize() {
|
||||||
|
return file.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() throws IOException {
|
||||||
|
try (InputStream is = new FileInputStream(file)) {
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
byte[] data = new byte[8192];
|
||||||
|
int nRead;
|
||||||
|
while ((nRead = is.read(data, 0, data.length)) != -1) {
|
||||||
|
buffer.write(data, 0, nRead);
|
||||||
|
}
|
||||||
|
return buffer.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return new FileInputStream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferTo(File dest) throws IOException {
|
||||||
|
try (InputStream in = new FileInputStream(file);
|
||||||
|
OutputStream out = new FileOutputStream(dest)) {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.File;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -26,6 +27,15 @@ public interface ISysOssService {
|
|||||||
|
|
||||||
SysOssVo upload(MultipartFile file, Long isPublic);
|
SysOssVo upload(MultipartFile file, Long isPublic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件流式上传(避免OOM)
|
||||||
|
*
|
||||||
|
* @param file 本地文件
|
||||||
|
* @param originalFileName 原始文件名(用于记录与后缀)
|
||||||
|
* @param isPublic 是否公开
|
||||||
|
*/
|
||||||
|
SysOssVo upload(File file, String originalFileName, Long isPublic);
|
||||||
|
|
||||||
void download(Long ossId, HttpServletResponse response) throws IOException;
|
void download(Long ossId, HttpServletResponse response) throws IOException;
|
||||||
|
|
||||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -147,6 +148,34 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
|||||||
return this.matchingUrl(sysOssVo);
|
return this.matchingUrl(sysOssVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SysOssVo upload(File file, String originalFileName, Long isPublic) {
|
||||||
|
// 1. 解析文件后缀
|
||||||
|
String suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."), originalFileName.length());
|
||||||
|
// 2. 获取OSS客户端
|
||||||
|
OssClient storage = OssFactory.instance();
|
||||||
|
UploadResult uploadResult;
|
||||||
|
try {
|
||||||
|
// 关键:用InputStream流式上传,而非加载整个文件到内存
|
||||||
|
uploadResult = storage.uploadSuffix(file, suffix);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException(e.getMessage());
|
||||||
|
}
|
||||||
|
SysOss oss = new SysOss();
|
||||||
|
oss.setUrl(uploadResult.getUrl());
|
||||||
|
oss.setFileSuffix(suffix);
|
||||||
|
oss.setFileName(uploadResult.getFilename());
|
||||||
|
oss.setOriginalName(originalFileName);
|
||||||
|
oss.setService(storage.getConfigKey());
|
||||||
|
oss.setCreateBy(LoginHelper.getNickName());
|
||||||
|
oss.setOwnerId(LoginHelper.getUserId());
|
||||||
|
oss.setIsPublic(isPublic == null ? 0 : isPublic);
|
||||||
|
baseMapper.insert(oss);
|
||||||
|
SysOssVo sysOssVo = new SysOssVo();
|
||||||
|
BeanCopyUtils.copy(oss, sysOssVo);
|
||||||
|
return this.matchingUrl(sysOssVo);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
|
|||||||
Reference in New Issue
Block a user