更新数据同步
This commit is contained in:
@@ -7,15 +7,19 @@ import com.mini.capi.biz.service.*;
|
||||
import com.mini.capi.model.info.FolderTree;
|
||||
import com.mini.capi.model.info.RunInfo;
|
||||
import com.mini.capi.utils.DateUtils;
|
||||
import com.mini.capi.utils.FileUtils;
|
||||
import com.mini.capi.utils.SqlUtils;
|
||||
import com.mini.capi.utils.vDate;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -170,18 +174,32 @@ public class viewController {
|
||||
List<BizFileFolders> childFolders = foldersService.list(childWrapper);
|
||||
folderTrees.add(new FolderTree(fileFolder, childFolders));
|
||||
}
|
||||
|
||||
QueryWrapper<BizFileFolders> uploadWrapper = new QueryWrapper<>();
|
||||
uploadWrapper.ne("parent_id", 0);
|
||||
List<BizFileFolders> uploadFolder = foldersService.list(uploadWrapper);
|
||||
|
||||
|
||||
model.addAttribute("files", files);
|
||||
model.addAttribute("uploadFolder", uploadFolder);
|
||||
model.addAttribute("folderTrees", folderTrees);
|
||||
return "file";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*/
|
||||
@GetMapping("/biz/downloadFile")
|
||||
public void downloadFile(HttpServletResponse response, String fileId) {
|
||||
try {
|
||||
BizFiles bizFile = filesService.getById(fileId);
|
||||
File file = new File(bizFile.getFileName());
|
||||
if (file.exists()) {
|
||||
FileUtils.downloadFile(response, file, bizFile.getFileName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统管理
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.mini.capi.utils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
@@ -78,116 +82,33 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
|
||||
public static MultipartFile[] getMultipartFiles(String filePath) {
|
||||
// 1. 校验路径合法性
|
||||
if (filePath == null || filePath.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("文件路径不能为空");
|
||||
/**
|
||||
* 下载文件核心方法
|
||||
*/
|
||||
public static void downloadFile(HttpServletResponse response, File file, String downloadFileName) throws IOException {
|
||||
// 1. 校验文件合法性
|
||||
if (file == null || !file.exists() || !file.isFile()) {
|
||||
throw new FileNotFoundException("文件不存在或不是有效文件:" + (file != null ? file.getAbsolutePath() : "null"));
|
||||
}
|
||||
File dir = new File(filePath);
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("目录不存在:" + filePath);
|
||||
if (!file.canRead()) {
|
||||
throw new IOException("文件无读取权限:" + file.getAbsolutePath());
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("路径不是目录:" + filePath);
|
||||
}
|
||||
|
||||
// 2. 筛选目录下的所有文件(排除子目录)
|
||||
File[] localFiles = dir.listFiles(File::isFile);
|
||||
if (localFiles == null || localFiles.length == 0) {
|
||||
return new MultipartFile[0];
|
||||
}
|
||||
|
||||
// 3. 包装本地File为MultipartFile并组装数组
|
||||
MultipartFile[] result = new MultipartFile[localFiles.length];
|
||||
for (int i = 0; i < localFiles.length; i++) {
|
||||
File file = localFiles[i];
|
||||
result[i] = new MultipartFile() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return file.getName(); // 文件名(与原始文件名一致,满足基础场景)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalFilename() {
|
||||
return file.getName(); // 原始文件名(本地文件即自身文件名)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
// 推断文件MIME类型,失败时返回默认二进制类型
|
||||
try {
|
||||
return Files.probeContentType(file.toPath());
|
||||
} catch (IOException e) {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return file.length() == 0; // 空文件判断(大小为0)
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return file.length(); // 文件大小(字节)
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() throws IOException {
|
||||
return Files.readAllBytes(file.toPath()); // 读取文件字节数组
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return Files.newInputStream(file.toPath()); // 获取文件输入流
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||
Files.copy(file.toPath(), dest.toPath()); // 复制文件到目标路径
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getFileType(String fileName) {
|
||||
if (fileName == null || fileName.isEmpty()) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
|
||||
switch (extension) {
|
||||
case "pdf":
|
||||
return "pdf";
|
||||
case "doc":
|
||||
case "docx":
|
||||
return "word";
|
||||
case "xls":
|
||||
case "xlsx":
|
||||
return "excel";
|
||||
case "ppt":
|
||||
case "pptx":
|
||||
return "ppt";
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "gif":
|
||||
return "image";
|
||||
case "mp4":
|
||||
case "avi":
|
||||
case "mov":
|
||||
return "video";
|
||||
case "mp3":
|
||||
case "wav":
|
||||
return "audio";
|
||||
default:
|
||||
// 如果没有扩展名,视为文件夹
|
||||
if (!fileName.contains(".")) {
|
||||
return "folder";
|
||||
}
|
||||
return "unknown";
|
||||
// 2. 设置响应头(解决中文乱码、触发下载对话框)
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 二进制流
|
||||
String fileName = downloadFileName != null ? downloadFileName : file.getName();
|
||||
// 文件名URLEncoder编码,兼容浏览器中文显示
|
||||
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
|
||||
response.setContentLengthLong(file.length()); // 设置文件大小(可选,优化体验)
|
||||
// 3. 流传输文件(try-with-resources自动关闭流)
|
||||
try (InputStream in = new FileInputStream(file);
|
||||
OutputStream out = response.getOutputStream()) {
|
||||
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区,提升传输效率
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
}
|
||||
out.flush(); // 强制刷出缓冲区数据
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,21 +198,31 @@
|
||||
<!-- 文件列表区域 -->
|
||||
<div class="flex-grow overflow-y-auto scrollbar-thin p-4">
|
||||
<div id="fileList" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<!-- 文件项1 -->
|
||||
<div class="bg-white rounded-lg shadow-card p-4 hover:shadow-hover transition-shadow"
|
||||
<!-- 文件项 - 循环渲染 -->
|
||||
<div class="bg-white rounded-lg shadow-card p-4 hover:shadow-hover transition-all duration-300 ease-in-out transform hover:-translate-y-1"
|
||||
th:each="file : ${files}">
|
||||
<div class="flex items-center mb-3">
|
||||
<!-- 文件图标与信息区域 -->
|
||||
<div class="flex items-start mb-3">
|
||||
<img src="https://p3-doubao-search-sign.byteimg.com/labis/fc16ba2cc6e083aacff2c3629bd48364~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1779099593&x-signature=ZOIIEOF4OonT%2B1U3qJMy0ggfOwM%3D"
|
||||
alt="文档" class="file-icon mr-3">
|
||||
<div class="flex-grow">
|
||||
<h3 class="font-medium truncate" th:text="${file.getFileName()}"></h3>
|
||||
<p class="text-xs text-text-secondary">2023-06-15 | 2.4MB</p>
|
||||
<div class="flex-grow min-w-0">
|
||||
<h3 class="font-medium text-gray-800 w-full whitespace-nowrap overflow-hidden text-ellipsis px-1"
|
||||
th:text="${file.getFileName()}"
|
||||
th:title="${file.getFileName()}">
|
||||
</h3>
|
||||
<p class="text-xs text-gray-500 mt-1 flex items-center justify-between">
|
||||
<span th:text="${file.getUploadTime()}"></span>
|
||||
<span th:text="${file.getFileSize()}"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button class="text-accent hover:text-blue-700 transition-colors"
|
||||
onclick="downloadFile('project-plan.docx')">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="flex justify-end pt-2 border-t border-gray-100">
|
||||
<button class="text-blue-500 hover:text-blue-700 transition-colors flex items-center px-2 py-1 rounded-md hover:bg-blue-50"
|
||||
th:onclick="downloadFile([(${file.getFileId()})])">
|
||||
<i class="fa fa-download mr-1 text-sm"></i>
|
||||
<span class="text-sm">下载</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -425,16 +435,10 @@
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
function downloadFile(fileName) {
|
||||
showMessage(`正在准备下载 ${fileName}...`);
|
||||
|
||||
// 这里应该是实际的下载API调用
|
||||
function downloadFile(fileId) {
|
||||
setTimeout(() => {
|
||||
// 模拟下载完成
|
||||
showMessage(`${fileName} 下载成功`);
|
||||
|
||||
// 实际项目中,这里应该调用后端API进行文件下载
|
||||
// 例如:window.location.href = `/api/download/${fileName}`;
|
||||
showMessage(`下载成功`);
|
||||
window.open('downloadFile?fileId=' + fileId, '_blank')
|
||||
}, 800);
|
||||
}
|
||||
|
||||
@@ -730,99 +734,6 @@
|
||||
toast.classList.add('translate-y-20', 'opacity-0');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 后端API接口方法(与Spring Boot联调)
|
||||
const api = {
|
||||
// 获取文件夹列表
|
||||
getFolders: function () {
|
||||
// 实际项目中,这里应该调用后端API获取文件夹列表
|
||||
// return fetch('/api/folders')
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('获取文件夹列表失败', 'error');
|
||||
// console.error(error);
|
||||
// return [];
|
||||
// });
|
||||
},
|
||||
|
||||
// 获取文件列表
|
||||
getFiles: function (folderId) {
|
||||
// 实际项目中,这里应该调用后端API获取文件列表
|
||||
// return fetch(`/api/folders/${folderId}/files`)
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('获取文件列表失败', 'error');
|
||||
// console.error(error);
|
||||
// return [];
|
||||
// });
|
||||
},
|
||||
|
||||
// 创建文件夹
|
||||
createFolder: function (name, parentId) {
|
||||
// 实际项目中,这里应该调用后端API创建文件夹
|
||||
// return fetch('/api/folders', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// name: name,
|
||||
// parentId: parentId
|
||||
// }),
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('创建文件夹失败', 'error');
|
||||
// console.error(error);
|
||||
// throw error;
|
||||
// });
|
||||
},
|
||||
|
||||
// 上传文件
|
||||
uploadFiles: function (files, folderId) {
|
||||
// 实际项目中,这里应该调用后端API上传文件
|
||||
// const formData = new FormData();
|
||||
// files.forEach(file => {
|
||||
// formData.append('files', file);
|
||||
// });
|
||||
// formData.append('folderId', folderId);
|
||||
//
|
||||
// return fetch('/api/upload', {
|
||||
// method: 'POST',
|
||||
// body: formData,
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('上传文件失败', 'error');
|
||||
// console.error(error);
|
||||
// throw error;
|
||||
// });
|
||||
},
|
||||
|
||||
// 下载文件
|
||||
downloadFile: function (fileId) {
|
||||
// 实际项目中,这里应该调用后端API下载文件
|
||||
// window.location.href = `/api/files/${fileId}/download`;
|
||||
},
|
||||
|
||||
// 删除文件
|
||||
deleteFile: function (fileId) {
|
||||
// 实际项目中,这里应该调用后端API删除文件
|
||||
// return fetch(`/api/files/${fileId}`, {
|
||||
// method: 'DELETE',
|
||||
// })
|
||||
// .then(response => {
|
||||
// if (!response.ok) {
|
||||
// throw new Error('删除文件失败');
|
||||
// }
|
||||
// })
|
||||
// .catch(error => {
|
||||
// showMessage('删除文件失败', 'error');
|
||||
// console.error(error);
|
||||
// throw error;
|
||||
// });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user