parent
df48131704
commit
33d3f19c4c
@ -0,0 +1,189 @@ |
|||||||
|
package com.alops.web.controller.LlmKnowledgeController; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
import com.alops.chat.entity.UploadedFile; |
||||||
|
import com.alops.chat.service.UploadedFileService; |
||||||
|
import com.alops.common.config.RuoYiConfig; |
||||||
|
import com.alops.common.core.controller.BaseController; |
||||||
|
import com.alops.common.utils.file.FileUtils; |
||||||
|
import com.alops.common.utils.file.FileTypeUtils; |
||||||
|
import com.alops.common.utils.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
import java.net.URLEncoder; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件下载和预览控制器 |
||||||
|
* |
||||||
|
* @author lingma |
||||||
|
*/ |
||||||
|
@RestController |
||||||
|
@RequestMapping("/QandA/knowledge/file") |
||||||
|
public class FileController extends BaseController { |
||||||
|
private static final Logger log = LoggerFactory.getLogger(FileController.class); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private UploadedFileService uploadedFileService; |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载文件 |
||||||
|
* |
||||||
|
* @param fileId 文件ID |
||||||
|
* @param response 响应对象 |
||||||
|
*/ |
||||||
|
@PreAuthorize("@ss.hasPermi('QandA:knowledge:filedownload')") |
||||||
|
@GetMapping("/download/{fileId}") |
||||||
|
public void download(@PathVariable Long fileId, HttpServletResponse response, HttpServletRequest request) { |
||||||
|
try { |
||||||
|
UploadedFile uploadedFile = uploadedFileService.findById(fileId); |
||||||
|
if (uploadedFile == null) { |
||||||
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取文件路径
|
||||||
|
String filePath = uploadedFile.getFilePath(); |
||||||
|
// 获取实际文件路径(处理配置文件前缀)
|
||||||
|
String realPath = RuoYiConfig.getProfile() + filePath.replaceFirst("/profile", ""); |
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
File file = new File(realPath); |
||||||
|
if (!file.exists()) { |
||||||
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 检查文件是否允许下载
|
||||||
|
if (!FileUtils.checkAllowDownload(realPath)) { |
||||||
|
throw new Exception(StringUtils.format("文件({})非法,不允许下载。", realPath)); |
||||||
|
} |
||||||
|
|
||||||
|
// 设置下载文件名(从文件路径中提取真实文件名)
|
||||||
|
String fileName = FileUtils.getName(realPath); |
||||||
|
if (StringUtils.isEmpty(fileName)) { |
||||||
|
fileName = uploadedFile.getSource(); |
||||||
|
// 如果source中也不包含扩展名,则尝试添加扩展名
|
||||||
|
String extension = uploadedFile.getFormat(); |
||||||
|
if (StringUtils.isNotEmpty(extension) && !fileName.endsWith("." + extension)) { |
||||||
|
fileName = fileName + "." + extension; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 对文件名进行编码以支持中文
|
||||||
|
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()) |
||||||
|
.replaceAll("\\+", "%20"); |
||||||
|
|
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); |
||||||
|
response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName); |
||||||
|
FileUtils.writeBytes(realPath, response.getOutputStream()); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("下载文件失败", e); |
||||||
|
try { |
||||||
|
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "下载文件失败: " + e.getMessage()); |
||||||
|
} catch (IOException ioException) { |
||||||
|
log.error("设置错误响应失败", ioException); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 预览文件 |
||||||
|
* |
||||||
|
* @param fileId 文件ID |
||||||
|
* @param response 响应对象 |
||||||
|
*/ |
||||||
|
@PreAuthorize("@ss.hasPermi('QandA:knowledge:filepreview')") |
||||||
|
@GetMapping("/preview/{fileId}") |
||||||
|
public void preview(@PathVariable Long fileId, HttpServletResponse response, HttpServletRequest request) { |
||||||
|
try { |
||||||
|
UploadedFile uploadedFile = uploadedFileService.findById(fileId); |
||||||
|
if (uploadedFile == null) { |
||||||
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取文件路径
|
||||||
|
String filePath = uploadedFile.getFilePath(); |
||||||
|
// 获取实际文件路径(处理配置文件前缀)
|
||||||
|
String realPath = RuoYiConfig.getProfile() + filePath.replaceFirst("/profile", ""); |
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
File file = new File(realPath); |
||||||
|
if (!file.exists()) { |
||||||
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 检查文件是否允许预览
|
||||||
|
if (!FileUtils.checkAllowDownload(realPath)) { |
||||||
|
throw new Exception(StringUtils.format("文件({})非法,不允许预览。", realPath)); |
||||||
|
} |
||||||
|
|
||||||
|
// 设置Content-Type以便浏览器预览
|
||||||
|
String fileExtension = FileTypeUtils.getFileType(uploadedFile.getSource()); |
||||||
|
if (StringUtils.isEmpty(fileExtension)) { |
||||||
|
fileExtension = uploadedFile.getFormat(); |
||||||
|
} |
||||||
|
String contentType = getContentTypeByExtension(fileExtension); |
||||||
|
if (contentType != null) { |
||||||
|
response.setContentType(contentType); |
||||||
|
} else { |
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); |
||||||
|
} |
||||||
|
|
||||||
|
FileUtils.writeBytes(realPath, response.getOutputStream()); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("预览文件失败", e); |
||||||
|
try { |
||||||
|
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "预览文件失败: " + e.getMessage()); |
||||||
|
} catch (IOException ioException) { |
||||||
|
log.error("设置错误响应失败", ioException); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据文件扩展名获取对应的Content-Type |
||||||
|
* |
||||||
|
* @param extension 文件扩展名 |
||||||
|
* @return Content-Type字符串 |
||||||
|
*/ |
||||||
|
private String getContentTypeByExtension(String extension) { |
||||||
|
if (StringUtils.isEmpty(extension)) { |
||||||
|
return MediaType.APPLICATION_OCTET_STREAM_VALUE; |
||||||
|
} |
||||||
|
|
||||||
|
switch (extension.toLowerCase()) { |
||||||
|
case "txt": |
||||||
|
return "text/plain;charset=UTF-8"; |
||||||
|
case "doc": |
||||||
|
return "application/msword"; |
||||||
|
case "docx": |
||||||
|
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; |
||||||
|
case "pdf": |
||||||
|
return "application/pdf"; |
||||||
|
case "jpg": |
||||||
|
case "jpeg": |
||||||
|
return "image/jpeg"; |
||||||
|
case "png": |
||||||
|
return "image/png"; |
||||||
|
case "gif": |
||||||
|
return "image/gif"; |
||||||
|
default: |
||||||
|
return MediaType.APPLICATION_OCTET_STREAM_VALUE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue