文件上传更改

This commit is contained in:
XaoLi717 2024-09-13 08:39:57 +08:00
parent d4f1e0e52e
commit cd2e22c7bc
4 changed files with 446 additions and 0 deletions

View File

@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.bpm.service.onlyoffice.document;
import cn.iocoder.yudao.module.bpm.controller.admin.onlyoffice.vo.Document;
import cn.iocoder.yudao.module.bpm.controller.admin.onlyoffice.vo.DocumentEditParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 文档业务 接口
* @author: xx
* @date: 2024/8/7 16:30
*/
public interface DocumentService {
/**
* 构建文档对象
* @param filePath
* @param fileName
* @return documentKey 文档key
*/
String buildDocument(String filePath, String fileName);
/**
* 从缓从中获取文档信息
* @param documentKey
* @return
*/
Document getDocument(String documentKey); /**
* 从缓从中获取文档信息
* @param documentKey
* @return
*/
Document getDocumentToken(String documentKey,String token);
/**
* 下载文档实体文件
* @param documentKey
* @param request
* @param response
* @throws IOException
*/
void downloadDocumentFile(String documentKey, HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* 构建文档编辑参数 对象
* @param userId
* @param userName
* @return
*/
DocumentEditParam buildDocumentEditParam(String userId, String userName, String fileName);
/**
* 构建文档编辑参数 对象
* @param userId
* @param userName
* @return
*/
DocumentEditParam buildDocumentEditParamToken(String userId, String userName, String fileName,String token);
/**
* 编辑后保存文档实体文件
* @param documentKey
* @param downloadUrl
* @throws IOException
*/
boolean saveDocumentFile(String documentKey, String downloadUrl) throws IOException;
/**
* 获取服务暴露的host(包含端口)
* @return
*/
Object getServerHost();
/**
* 文档是否支持编辑
* @param document
* @return
*/
boolean canEdit(Document document);
}

View File

@ -0,0 +1,334 @@
package cn.iocoder.yudao.module.bpm.service.onlyoffice.document;
import cn.iocoder.yudao.module.bpm.controller.admin.onlyoffice.utils.DocumentConstants;
import cn.iocoder.yudao.module.bpm.controller.admin.onlyoffice.utils.Md5Utils;
import cn.iocoder.yudao.module.bpm.controller.admin.onlyoffice.vo.Document;
import cn.iocoder.yudao.module.bpm.controller.admin.onlyoffice.vo.DocumentEditParam;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hashids.Hashids;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
@Slf4j
@Service
public class DocumentServiceImpl implements DocumentService{
@Autowired
Environment environment;
@Value("${document.server.host}")
private String serverHost ;
/**
* 大小限制默认10M
*/
@Value("${document.file-size.limit:10485760}")
private Long docFileSizeLimit;
@Value("${files.docservice.url.site}")
private String documentServerHost;
@Value("${files.docservice.url.api}")
private String documentServerApiJs;
// @Autowired
//private DocumentCacheService cacheService;
@Override
public String buildDocument(String filePath, String fileName) {
if (StringUtils.isBlank(filePath)) {
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_NOT_EXISTS);
}
filePath = FilenameUtils.normalize(filePath);
String fileType = StringUtils.lowerCase(FilenameUtils.getExtension(filePath));
if (StringUtils.isBlank(fileType)) {
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_NO_EXTENSION);
}
// 如果指定了文件名则需要校验和实体文件格式是否一致
if (StringUtils.isNotBlank(fileName) && !fileType.equalsIgnoreCase(FilenameUtils.getExtension(fileName))) {
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_EXTENSION_NOT_MATCH);
}
File docFile = new File(filePath);
// 校验文件实体
preFileCheck(docFile);
fileName = StringUtils.isNotBlank(fileName) ? fileName : docFile.getName();
String fileKey = this.fileKey(docFile, fileName);
Document document = Document.builder()
.fileType(fileType)
.title(fileName)
.storage(filePath)
.build();
boolean cached = false;
// try {
// cached = cacheService.put(fileKey, document);
// } catch (Exception e) {
// log.error("$$$ 缓存失败~~", e);
// }
// if (!cached) {
// throw new DocumentException(ErrorCodeEnum.DOC_CACHE_ERROR);
// }
document.setKey(fileKey);
return JSON.toJSONString(document);
}
@Override
public Document getDocument(String documentKey ) {
Document doc = null;
try {
doc = JSON.parseObject(documentKey,Document.class);
} catch (Exception e) {
log.error("$$$ 获取缓存失败~~", e);
}
if (doc == null) {
// throw new DocumentException(ErrorCodeEnum.DOC_CACHE_NOT_EXISTS);
}
// 从缓存中取出后再绑定非必需缓存字段节省缓存大小
// doc.setKey(documentKey);
doc.setUrl(fileUrl(doc.getTitle()));
if (log.isDebugEnabled()) {
log.info(doc.toString());
}
return doc;
}
@Override
public Document getDocumentToken(String documentKey,String token) {
Document doc = null;
try {
doc = JSON.parseObject(documentKey,Document.class);
} catch (Exception e) {
log.error("$$$ 获取缓存失败~~", e);
}
if (doc == null) {
// throw new DocumentException(ErrorCodeEnum.DOC_CACHE_NOT_EXISTS);
}
// 从缓存中取出后再绑定非必需缓存字段节省缓存大小
// doc.setKey(documentKey);
doc.setUrl(fileUrlToken(doc.getTitle(),token));
if (log.isDebugEnabled()) {
log.info(doc.toString());
}
return doc;
}
/**
* 计算文件key值: 文件md5值+路径的短md5值+名称的短md5值
* @param docFile
* @param name 生成协作时文档的docuemnt.key的值
* @return
*/
public String fileKey(File docFile, String name) {
String docFileMd5 = Md5Utils.getFileMd5(docFile);
if (StringUtils.isBlank(docFileMd5)) {
log.error("$$$ 构建文件信息失败!计算文件 md5 失败!");
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_MD5_ERROR);
}
String pathShortMd5 = Md5Utils.md5(docFile.getAbsolutePath());
String nameShortMd5 = Md5Utils.md5(name);
Hashids hashids = new Hashids(DocumentConstants.HASH_KEY);
// (将路径字符串短md5值 + 名称字符串短md5值) ==> 再转成短id形式 ==> 作为文档的key暂且认为是不会重复的
String key = hashids.encodeHex(String.format("%s%s%s", docFileMd5,pathShortMd5, nameShortMd5));
if (StringUtils.isBlank(key)) {
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_KEY_ERROR);
}
return key;
}
/**
* 文件key值
* @param fileType
* @param docCrc32
* @return
*/
private String fileKey(String fileType, String docCrc32) {
return String.format("%s_%s", fileType, docCrc32);
}
/**
* 文件url地址
* @param
* @return
*/
private String fileUrl(String filename) {
return String.format(DocumentConstants.OFFICE_API_DOC_FILE, getServerHost(), "?name="+filename);
// return "http://192.168.0.58:20053/download?name="+filename;
}
/**
* 文件url地址
* @param
* @return
*/
private String fileUrlToken(String filename,String token) {
return String.format(DocumentConstants.OFFICE_API_DOC_FILE, getServerHost(), "?name="+filename+"&token="+token);
// return "http://192.168.0.58:20053/download?name="+filename;
}
/**
* 根据文档信息下载文档文件
* @param documentKey
* @param request
* @param response
* @throws IOException
*/
@Override
public void downloadDocumentFile(String documentKey, HttpServletRequest request, HttpServletResponse response) throws IOException {
Document doc = this.getDocument(documentKey);
File file = new File(doc.getStorage());
try (InputStream reader = new FileInputStream(file);
OutputStream out = response.getOutputStream()) {
byte[] buf = new byte[(int) FileUtils.ONE_KB];
int len = 0;
//response.setContentType(mimeType(file));
while ((len = reader.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.flush();
} catch (Exception e) {
log.error("下载失败!读取文件[" + doc.getStorage() + "]报错~~", e);
}
}
@Override
public DocumentEditParam buildDocumentEditParam(String userId, String userName, String fileName) {
return DocumentEditParam.builder()
// .callbackUrl(callbackUrl(fileName)+"&token="+token)
.callbackUrl(callbackUrl(fileName))
.user(DocumentEditParam.UserBean.builder()
.id(userId)
.name(userName)
.build())
.build();
}
@Override
public DocumentEditParam buildDocumentEditParamToken(String userId, String userName, String fileName,String token) {
return DocumentEditParam.builder()
.callbackUrl(callbackUrl(fileName)+"&token="+token)
// .callbackUrl(callbackUrl(fileName))
.user(DocumentEditParam.UserBean.builder()
.id(userId)
.name(userName)
.build())
.build();
}
private String callbackUrl(String fileName) {
String format = String.format(DocumentConstants.OFFICE_API_CALLBACK, getServerHost());
format=format+"?fileName="+fileName;
return format;
}
/**
* 上传文档实体文件
* @param documentKey
* @param downloadUrl
* @throws IOException
*/
@Override
public boolean saveDocumentFile(String documentKey, String downloadUrl) {
if (log.isInfoEnabled()) {
log.info(downloadUrl);
}
// TODO 默认覆盖源文件如果调用者指定则存到临时目录
boolean isCover = true;
Document doc = this.getDocument(documentKey);
String saveFilePath = doc.getStorage();
// if (!isCover) {
// String baseDir = environment.getProperty("java.io.tmpdir");
// saveFilePath = String.format("%s/office-api/%s/%s.%s", baseDir, documentKey, System.currentTimeMillis(), doc.getFileType());
// }
File saveFile = new File(saveFilePath);
boolean success = false;
try {
FileUtils.copyURLToFile(new URL(downloadUrl), saveFile);
if (saveFile.exists() && saveFile.length() > 0) {
success = true;
}
} catch (IOException e) {
log.error("$$$ 保存文档失败!", e);
}
return success;
//TODO 编辑成功后应该删除之前的编辑状态缓存
}
@Override
public Object getServerHost() {
if (StringUtils.startsWith(serverHost, DocumentConstants.HTTP_SCHEME)) {
return serverHost;
}
return String.format("http://%s", serverHost);
}
@Override
public boolean canEdit(Document document) {
if (ArrayUtils.contains(DocumentConstants.FILE_TYPE_UNSUPPORT_EDIT, document.getFileType())) {
return false;
}
return true;
}
/**
* 获取文档信息api地址
* @param docId
* @return
*/
private String docInfoUrl(String docId) {
return String.format(DocumentConstants.OFFICE_API_DOC, getServerHost(), docId);
}
/**
* 获取文件的 mimetype
* @param file
* @deprecated
* @return
*/
@Deprecated
private String mimeType(File file) {
try {
return Files.probeContentType(Paths.get(file.toURI()));
} catch (IOException e) {
log.error("$$$ 获取文件mimeType错误", e);
}
return null;
}
/**
* 先校验文档文件
* @param docFile
* @return
*/
private void preFileCheck(File docFile) {
if (log.isDebugEnabled()) {
log.debug("### 开始校验文档:[{}]", docFile.getAbsolutePath());
}
if (docFile == null || !docFile.exists()) {
log.error("$$$ 目标文档不存在,无法打开!");
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_NOT_EXISTS);
}
if (docFile.isDirectory() || docFile.length() <= 0) {
log.error("$$$ 目标文档[{}]是目录或空文件,无法打开!", docFile.getAbsolutePath());
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_EMPTY);
}
if (!docFile.canRead()) {
log.error("$$$ 目标文档[{}]不可读,无法打开!", docFile.getAbsolutePath());
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_UNREADABLE);
}
if (docFile.length() > docFileSizeLimit) {
log.error("$$$ 目标文档大小超过限制({}B > {}B),无法打开!", docFile.length(), docFileSizeLimit);
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_OVERSIZE);
}
String ext = StringUtils.lowerCase(FilenameUtils.getExtension(docFile.getName()));
if (!ArrayUtils.contains(DocumentConstants.FILE_TYPE_SUPPORT_VIEW, ext)) {
log.error("$$$ 目标文档格式[{}]不正确,无法打开!(只支持:{}",
ext, StringUtils.join(DocumentConstants.FILE_TYPE_SUPPORT_VIEW, ","));
// throw new DocumentException(ErrorCodeEnum.DOC_FILE_TYPE_UNSUPPORTED);
}
}
}

View File

@ -0,0 +1,10 @@
package cn.iocoder.yudao.module.bpm.service.onlyoffice.fileupload;
import cn.iocoder.yudao.module.bpm.dal.dataobject.onlyoffice.onlyofficeDO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface FileUploadService extends IService<onlyofficeDO> {
List<onlyofficeDO> list();
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.bpm.service.onlyoffice.fileupload;
import cn.iocoder.yudao.module.bpm.dal.dataobject.onlyoffice.onlyofficeDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.onlyoffice.FileUploadMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class FileUploadServiceImpl extends ServiceImpl<FileUploadMapper, onlyofficeDO> implements FileUploadService {
@Autowired
FileUploadMapper uploadMapper;
@Override
public List<onlyofficeDO> list() {
return uploadMapper.selectList(null);
}
}