文件上传更改
This commit is contained in:
parent
d4f1e0e52e
commit
cd2e22c7bc
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user