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