diff --git a/pom.xml b/pom.xml index d24d6fd..260f25c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,12 +6,12 @@ com.qiwenshare qiwenshare - 1.0.3 + 1.0.4 com.qiwenshare qiwen-file - 1.0.3-SNAPSHOT + 1.0.4-SNAPSHOT qiwen-file fileos.qiwenshare.com jar @@ -110,6 +110,12 @@ spring-boot-starter-data-elasticsearch + + com.inversoft + prime-jwt + 1.3.1 + + diff --git a/src/main/java/com/qiwenshare/file/api/IUserFileService.java b/src/main/java/com/qiwenshare/file/api/IUserFileService.java index 1f5d2ee..b6500f0 100644 --- a/src/main/java/com/qiwenshare/file/api/IUserFileService.java +++ b/src/main/java/com/qiwenshare/file/api/IUserFileService.java @@ -11,6 +11,7 @@ import java.util.Map; public interface IUserFileService extends IService { List selectUserFileByNameAndPath(String fileName, String filePath, Long userId); + List selectSameUserFile(String fileName, String filePath, String extendName, Long userId); void replaceUserFilePath(String filePath, String oldFilePath, Long userId); List userFileList(UserFile userFile, Long beginCount, Long pageCount); void updateFilepathByFilepath(String oldfilePath, String newfilePath, String fileName, String extendName, long userId); diff --git a/src/main/java/com/qiwenshare/file/config/WebMvcConfig.java b/src/main/java/com/qiwenshare/file/config/WebMvcConfig.java index bfd7a38..76d8f60 100644 --- a/src/main/java/com/qiwenshare/file/config/WebMvcConfig.java +++ b/src/main/java/com/qiwenshare/file/config/WebMvcConfig.java @@ -44,16 +44,14 @@ public class WebMvcConfig implements WebMvcConfigurer { list.add("/file/**"); list.add("/filetransfer/**"); list.add("/recoveryfile/**"); - list.add("/share/**"); + list.add("/share/sharefile"); + list.add("/share/savesharefile"); + list.add("/share/shareList"); registry.addInterceptor(authenticationInterceptor) .addPathPatterns(list) .excludePathPatterns("/file", "/filetransfer/downloadfile", - "/filetransfer/preview", - "/share/sharefileList", - "/share/sharetype", - "/share/checkextractioncode", - "/share/checkendtime"); + "/filetransfer/preview"); } } \ No newline at end of file diff --git a/src/main/java/com/qiwenshare/file/controller/OfficeController.java b/src/main/java/com/qiwenshare/file/controller/OfficeController.java new file mode 100644 index 0000000..6cf6243 --- /dev/null +++ b/src/main/java/com/qiwenshare/file/controller/OfficeController.java @@ -0,0 +1,331 @@ +package com.qiwenshare.file.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.qiwenshare.common.exception.NotLoginException; +import com.qiwenshare.common.result.RestResult; +import com.qiwenshare.common.util.DateUtil; +import com.qiwenshare.file.anno.MyLog; +import com.qiwenshare.file.api.IFileService; +import com.qiwenshare.file.api.IFiletransferService; +import com.qiwenshare.file.api.IUserFileService; +import com.qiwenshare.file.api.IUserService; +import com.qiwenshare.file.domain.*; +import com.qiwenshare.file.dto.file.CreateOfficeFileDTO; +import com.qiwenshare.file.dto.file.EditOfficeFileDTO; +import com.qiwenshare.file.dto.file.PreviewOfficeFileDTO; +import com.qiwenshare.file.helper.ConfigManager; +import com.qiwenshare.ufo.factory.UFOFactory; +import com.qiwenshare.ufo.operation.write.Writer; +import com.qiwenshare.ufo.operation.write.domain.WriteFile; +import com.qiwenshare.ufo.util.PathUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.*; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Scanner; +import java.util.UUID; + +@Tag(name = "office", description = "该接口为Onlyoffice文件操作接口,主要用来做一些文档的编辑,浏览等。") +@RestController +@Slf4j +@RequestMapping({"/office"}) +public class OfficeController { + public static final String CURRENT_MODULE = "Onlyoffice文件操作接口"; + @Resource + IUserService userService; + @Resource + UFOFactory ufoFactory; + + @Value("${deployment.host}") + private String deploymentHost; + + + @Resource + IFileService fileService; + @Resource + IUserFileService userFileService; + + @ResponseBody + @RequestMapping(value = "/createofficefile", method = RequestMethod.POST) + public RestResult createOfficeFile(HttpServletRequest request, @RequestBody CreateOfficeFileDTO createOfficeFileDTO, @RequestHeader("token") String token) { + RestResult result = new RestResult<>(); + try{ + + UserBean loginUser = userService.getUserBeanByToken(token); + if (loginUser == null) { + throw new NotLoginException(); + } + String fileName = createOfficeFileDTO.getFileName(); + String filePath = createOfficeFileDTO.getFilePath(); + String extendName = createOfficeFileDTO.getExtendName(); + List userFiles = userFileService.selectSameUserFile(fileName, filePath, extendName, loginUser.getUserId()); + if (userFiles != null && !userFiles.isEmpty()) { + return RestResult.fail().message("同名文件已存在"); + } + String uuid = UUID.randomUUID().toString().replaceAll("-",""); + + SimpleDateFormat formater = new SimpleDateFormat("yyyyMMdd"); + + + String fileSavePath = PathUtil.getStaticPath() + "/create/" + formater.format(new Date()); + File fileSavePathFile = new File(fileSavePath); + + if (!fileSavePathFile.exists()) { + fileSavePathFile.mkdirs(); + } + String fileUrl = "/create/" + formater.format(new Date()) + "/" + uuid + "." + extendName; + + File file = new File(fileSavePath + "/" + uuid + "." + extendName); + if(!file.exists()){ + try { + if("docx".equals(extendName)){ + //创建word文档 + XWPFDocument document= new XWPFDocument(); + //Write the Document in file system + FileOutputStream out = new FileOutputStream(file); + document.write(out); + out.close(); + }else if("xlsx".equals(extendName)){ + //创建excel表格 + XSSFWorkbook workbook = new XSSFWorkbook(); + //创建工作表 + workbook.createSheet("Sheet1"); + //Write the Document in file system + FileOutputStream out = new FileOutputStream(file); + workbook.write(out); + out.close(); + }else if("pptx".equals(extendName)){ + //创建pptx演示文稿 + XMLSlideShow pptx = new XMLSlideShow(); + //创建工作表 + //Write the Document in file system + FileOutputStream out = new FileOutputStream(file); + pptx.write(out); + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + FileBean fileBean = new FileBean(); + fileBean.setFileSize(Long.valueOf(0)); + fileBean.setFileUrl(fileUrl); + fileBean.setStorageType(0); + fileBean.setPointCount(1); + fileBean.setIdentifier(uuid); + fileBean.setTimeStampName(uuid); + boolean saveFlag = fileService.save(fileBean); + UserFile userFile = new UserFile(); + if(saveFlag) { + userFile.setUserId(loginUser.getUserId()); + userFile.setFileName(fileName); + userFile.setFilePath(filePath); + userFile.setDeleteFlag(0); + userFile.setIsDir(0); + userFile.setExtendName(extendName); + userFile.setUploadTime(DateUtil.getCurrentTime()); + userFile.setFileId(fileBean.getFileId()); + userFileService.save(userFile); + } + Long newFileSize = file.length(); + //更新文件修改信息 + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>(); + FileInputStream fins = new FileInputStream(fileSavePath); + String md5Str = DigestUtils.md5Hex(fins); + fins.close(); + + lambdaUpdateWrapper + .set(FileBean::getIdentifier, md5Str) + .set(FileBean::getTimeStampName, md5Str) + .set(FileBean::getFileSize, newFileSize) + .eq(FileBean::getFileId, fileBean.getFileId()); + fileService.update(lambdaUpdateWrapper); + + result.success(); + result.setMessage("文件创建成功!"); + } catch (Exception e) { + log.error(e.getMessage()); + result.setCode(500); + result.setMessage("服务器错误!"); + } + return result; + } + + @Operation(summary = "预览office文件", description = "预览office文件", tags = {"office"}) + @MyLog(operation = "查看报告接口", module = CURRENT_MODULE) + @RequestMapping(value = "/previewofficefile", method = RequestMethod.POST) + @ResponseBody + public RestResult previewOfficeFile(HttpServletRequest request, @RequestBody PreviewOfficeFileDTO previewOfficeFileDTO, @RequestHeader("token") String token) { + RestResult result = new RestResult<>(); + try { + + UserBean loginUser = userService.getUserBeanByToken(token); + if (loginUser == null) { + throw new NotLoginException(); + } + UserFile userFile = userFileService.getById(previewOfficeFileDTO.getUserFileId()); + + String baseUrl = request.getScheme()+"://"+ deploymentHost + request.getContextPath(); + + FileModel file = new FileModel(userFile.getFileName() + "." + userFile.getExtendName(), + previewOfficeFileDTO.getPreviewUrl(), + String.valueOf(new Date().getTime()), + String.valueOf(loginUser.getUserId()), + loginUser.getUsername(), + "view"); + + String query = "?type=show&token="+token; + file.editorConfig.callbackUrl= baseUrl + "/office/IndexServlet" + query; + JSONObject jsonObject = new JSONObject(); + jsonObject.put("file",file); + jsonObject.put("docserviceApiUrl", ConfigManager.GetProperty("files.docservice.url.site") + ConfigManager.GetProperty("files.docservice.url.api")); + jsonObject.put("reportName",userFile.getFileName()); + result.setData(jsonObject); + result.setCode(200); + result.setMessage("获取报告成功!"); + } catch (Exception e) { + log.error(e.getMessage()); + result.setCode(500); + result.setMessage("服务器错误!"); + } + return result; + } + + @ResponseBody + @RequestMapping(value = "/editofficefile", method = RequestMethod.POST) + public RestResult editOfficeFile(HttpServletRequest request, @RequestBody EditOfficeFileDTO editOfficeFileDTO, @RequestHeader("token") String token) { + RestResult result = new RestResult<>(); + log.info("editOfficeFile"); + try { + + UserBean loginUser = userService.getUserBeanByToken(token); + if (loginUser == null) { + throw new NotLoginException(); + } + UserFile userFile = userFileService.getById(editOfficeFileDTO.getUserFileId()); + + String baseUrl = request.getScheme()+"://"+ deploymentHost + request.getContextPath(); + + log.info("回调地址baseUrl:" + baseUrl); + + FileModel file = new FileModel(userFile.getFileName() + "." + userFile.getExtendName(), + editOfficeFileDTO.getPreviewUrl(), + String.valueOf(new Date().getTime()), + String.valueOf(loginUser.getUserId()), + loginUser.getUsername(), + "edit"); + file.changeType(request.getParameter("mode"), "edit"); + + String query = "?type=edit&fileId="+userFile.getFileId()+"&token="+token; + file.editorConfig.callbackUrl= baseUrl + "/office/IndexServlet" + query; + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("file",file); + jsonObject.put("docserviceApiUrl",ConfigManager.GetProperty("files.docservice.url.site") + ConfigManager.GetProperty("files.docservice.url.api")); + jsonObject.put("reportName",userFile.getFileName()); + result.setData(jsonObject); + result.setCode(200); + result.setMessage("编辑报告成功!"); + } catch (Exception e) { + log.error(e.getMessage()); + result.setCode(500); + result.setMessage("服务器错误!"); + } + return result; + } + + + @RequestMapping(value = "/IndexServlet", method = RequestMethod.POST) + @ResponseBody + public void IndexServlet(HttpServletResponse response, HttpServletRequest request) throws IOException { + String token = request.getParameter("token"); + UserBean loginUser = userService.getUserBeanByToken(token); + if (loginUser == null) { + throw new NotLoginException(); + } + PrintWriter writer = null; + JSONObject jsonObj=null; + try { + writer = response.getWriter(); + Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A"); + String body = scanner.hasNext() ? scanner.next() : ""; + + jsonObj = JSON.parseObject(body); + log.debug("===saveeditedfile:" + jsonObj.get("status")) ; + String status = jsonObj!=null?jsonObj.get("status").toString():""; + if ("2".equals(status)) {//新建报告不强制手动操作时状态为2 + String type = request.getParameter("type"); + String downloadUri = (String) jsonObj.get("url"); + + if("edit".equals(type)){//修改报告 + log.debug("====文档编辑完成,现在开始保存编辑后的文档,其下载地址为:" + downloadUri); + String fileId = request.getParameter("fileId"); + String userFileId = request.getParameter("userFileId"); + FileBean fileBean = fileService.getById(fileId); + if (fileBean.getPointCount() > 1) { + //该场景,暂不支持编辑修改 + writer.write("{\"error\":1}"); + return ; + } + + URL url = new URL(downloadUri); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + InputStream stream = connection.getInputStream(); + + Writer writer1 = ufoFactory.getWriter(fileBean.getStorageType()); + WriteFile writeFile = new WriteFile(); + writeFile.setFileUrl(fileBean.getFileUrl()); + int fileLength = connection.getContentLength(); + log.info("当前修改文件大小为:" + fileLength); + writeFile.setFileSize(connection.getContentLength()); + writer1.write(stream, writeFile); + + //更新文件修改信息 + LambdaUpdateWrapper userFileUpdateWrapper = new LambdaUpdateWrapper<>(); + userFileUpdateWrapper + .set(UserFile::getUploadTime, DateUtil.getCurrentTime()) + .eq(UserFile::getUserFileId, userFileId); + userFileService.update(userFileUpdateWrapper); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>(); + String md5Str = DigestUtils.md5Hex(stream); + log.info("当前修改文件大小为2222:" + Long.valueOf(fileLength)); + lambdaUpdateWrapper + .set(FileBean::getIdentifier, md5Str) + .set(FileBean::getFileSize, Long.valueOf(fileLength)) + .eq(FileBean::getFileId, fileId); + fileService.update(lambdaUpdateWrapper); + + connection.disconnect(); + } + + } + }catch (Exception e) { + log.error(e.getMessage()); + } + String status = jsonObj!=null?jsonObj.get("status").toString():""; + if("3".equals(status)||"7".equals(status)) {//不强制手动保存时为6,"6".equals(status) + log.debug("====保存失败:"); + writer.write("{\"error\":1}"); + }else { + log.debug("状态为:0") ; + writer.write("{\"error\":" + "0" + "}"); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/qiwenshare/file/domain/FileModel.java b/src/main/java/com/qiwenshare/file/domain/FileModel.java new file mode 100644 index 0000000..73314f3 --- /dev/null +++ b/src/main/java/com/qiwenshare/file/domain/FileModel.java @@ -0,0 +1,315 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.domain; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.qiwenshare.file.helper.DocumentManager; +import com.qiwenshare.file.helper.FileUtility; +import com.qiwenshare.file.helper.ServiceConverter; +import lombok.Data; + +import java.io.File; +import java.io.FileInputStream; +import java.util.*; + +@Data +public class FileModel +{ + public String type = "desktop"; + public String mode = "edit"; + public String documentType; + public Document document; + public EditorConfig editorConfig; + public String token; + + public FileModel(String fileName, String fileUrl, String fileModifyTime, String uid, String uname, String mode) + { + if (fileName == null) fileName = ""; + fileName = fileName.trim(); + + documentType = FileUtility.GetFileType(fileName).toString().toLowerCase(); + + document = new Document(); + document.title = fileName; + document.url = fileUrl; +// document.urlUser = DocumentManager.GetFileUri(fileName, false); + document.fileType = FileUtility.GetFileExtension(fileName).replace(".", ""); + document.key = ServiceConverter.GenerateRevisionId(DocumentManager.CurUserHostAddress(null) + "/" + fileName + "/" + fileModifyTime); + document.info = new Info(); + document.info.favorite = uid != null && !uid.isEmpty() ? uid.equals("uid-2") : null; + + editorConfig = new EditorConfig(null); +// editorConfig.callbackUrl = DocumentManager.GetCallback(fileName); +// editorConfig.lang = null; + + if (uid != null) editorConfig.user.id = uid; + if (uname != null) editorConfig.user.name = uid.equals("uid-0") ? null : uname; + if (editorConfig.user.id.equals("uid-2")) editorConfig.user.group = "group-2"; + if (editorConfig.user.id.equals("uid-3")) editorConfig.user.group = "group-3"; + +// editorConfig.customization.goback.url = DocumentManager.GetServerUrl(false) + "/IndexServlet"; + + changeType(mode, type); + } + + public void changeType(String _mode, String _type) + { + if (_mode != null) mode = _mode; + if (_type != null) type = _type; + + Boolean canEdit = DocumentManager.GetEditedExts().contains(FileUtility.GetFileExtension(document.title)); + editorConfig.customization.submitForm = canEdit && (mode.equals("edit") || mode.equals("fillForms")); + editorConfig.mode = canEdit && !mode.equals("view") ? "edit" : "view"; + + document.permissions = new Permissions(mode, type, canEdit); + + if (type.equals("embedded")) InitDesktop(); + } + + public void InitDesktop() + { + editorConfig.InitDesktop(document.urlUser); + } + + public void BuildToken() + { + Map map = new HashMap<>(); + map.put("type", type); + map.put("documentType", documentType); + map.put("document", document); + map.put("editorConfig", editorConfig); + + token = DocumentManager.CreateToken(map); + } + + public String[] GetHistory() + { + String histDir = DocumentManager.HistoryDir(DocumentManager.StoragePath(document.title, null)); + if (DocumentManager.GetFileVersion(histDir) > 0) { + Integer curVer = DocumentManager.GetFileVersion(histDir); + + List hist = new ArrayList<>(); + Map histData = new HashMap(); + + for (Integer i = 1; i <= curVer; i++) { + Map obj = new HashMap(); + Map dataObj = new HashMap(); + String verDir = DocumentManager.VersionDir(histDir, i); + + try { + String key = null; + + key = i == curVer ? document.key : readFileToEnd(new File(verDir + File.separator + "key.txt")); + + obj.put("key", key); + obj.put("version", i); + + if (i == 1) { + String createdInfo = readFileToEnd(new File(histDir + File.separator + "createdInfo.json")); + JSONObject json = JSONObject.parseObject(createdInfo); + obj.put("created", json.get("created")); + Map user = new HashMap(); + user.put("id", json.get("id")); + user.put("name", json.get("name")); + obj.put("user", user); + } + + dataObj.put("key", key); + dataObj.put("url", i == curVer ? document.url : DocumentManager.GetPathUri(verDir + File.separator + "prev" + FileUtility.GetFileExtension(document.title))); + dataObj.put("version", i); + + if (i > 1) { + JSONObject changes = JSONObject.parseObject(readFileToEnd(new File(DocumentManager.VersionDir(histDir, i - 1) + File.separator + "changes.json"))); + JSONObject change = (JSONObject) ((JSONArray) changes.get("changes")).get(0); + + obj.put("changes", changes.get("changes")); + obj.put("serverVersion", changes.get("serverVersion")); + obj.put("created", change.get("created")); + obj.put("user", change.get("user")); + + Map prev = (Map) histData.get(Integer.toString(i - 2)); + Map prevInfo = new HashMap(); + prevInfo.put("key", prev.get("key")); + prevInfo.put("url", prev.get("url")); + dataObj.put("previous", prevInfo); + dataObj.put("changesUrl", DocumentManager.GetPathUri(DocumentManager.VersionDir(histDir, i - 1) + File.separator + "diff.zip")); + } + + if (DocumentManager.TokenEnabled()) + { + dataObj.put("token", DocumentManager.CreateToken(dataObj)); + } + + hist.add(obj); + histData.put(Integer.toString(i - 1), dataObj); + + } catch (Exception ex) { } + } + + Map histObj = new HashMap(); + histObj.put("currentVersion", curVer); + histObj.put("history", hist); + + Gson gson = new Gson(); + return new String[] { gson.toJson(histObj), gson.toJson(histData) }; + } + return new String[] { "", "" }; + } + + private String readFileToEnd(File file) { + String output = ""; + try { + try(FileInputStream is = new FileInputStream(file)) + { + Scanner scanner = new Scanner(is); + scanner.useDelimiter("\\A"); + while (scanner.hasNext()) { + output += scanner.next(); + } + scanner.close(); + } + } catch (Exception e) { } + return output; + } + + @Data + public class Document + { + public String title; + public String url; + public String urlUser; + public String fileType; + public String key; + public Info info; + public Permissions permissions; + } + + @Data + public class Permissions + { + public Boolean comment; + public Boolean download; + public Boolean edit; + public Boolean fillForms; + public Boolean modifyFilter; + public Boolean modifyContentControl; + public Boolean review; + public List reviewGroups; + + public Permissions(String mode, String type, Boolean canEdit) + { + comment = !mode.equals("view") && !mode.equals("fillForms") && !mode.equals("embedded") && !mode.equals("blockcontent"); + download = true; + edit = canEdit && (mode.equals("edit") || mode.equals("view") || mode.equals("filter") || mode.equals("blockcontent")); + fillForms = !mode.equals("view") && !mode.equals("comment") && !mode.equals("embedded") && !mode.equals("blockcontent"); + modifyFilter = !mode.equals("filter"); + modifyContentControl = !mode.equals("blockcontent"); + review = mode.equals("edit") || mode.equals("review"); + reviewGroups = editorConfig.user.group != null ? GetReviewGroups(editorConfig.user.group) : null; + } + + private List GetReviewGroups(String group){ + Map> reviewGroups = new HashMap<>(); + + reviewGroups.put("group-2", Arrays.asList("group-2", "")); + reviewGroups.put("group-3", Arrays.asList("group-2")); + + return reviewGroups.get(group); + } + } + + @Data + public class Info + { + Boolean favorite; + } + + public class EditorConfig + { + public HashMap actionLink = null; + public String mode = "edit"; + public String callbackUrl; + public String lang = "en"; + public User user; + public Customization customization; + public Embedded embedded; + + public EditorConfig(String actionData) + { + if (actionData != null) { + Gson gson = new Gson(); + actionLink = gson.fromJson(actionData, new TypeToken>() { }.getType()); + } + user = new User(); + customization = new Customization(); + } + + public void InitDesktop(String url) + { + embedded = new Embedded(); + embedded.saveUrl = url; + embedded.embedUrl = url; + embedded.shareUrl = url; + embedded.toolbarDocked = "top"; + } + + public class User + { + public String id = "uid-1"; + public String name = "John Smith"; + public String group = null; + } + + public class Customization + { + public Goback goback; + public Boolean forcesave; + public Boolean submitForm; + + public Customization() + { + forcesave = false; + goback = new Goback(); + } + + public class Goback + { + public String url; + } + } + + public class Embedded + { + public String saveUrl; + public String embedUrl; + public String shareUrl; + public String toolbarDocked; + } + } + + + public static String Serialize(FileModel model) + { + Gson gson = new Gson(); + return gson.toJson(model); + } +} diff --git a/src/main/java/com/qiwenshare/file/domain/FileType.java b/src/main/java/com/qiwenshare/file/domain/FileType.java new file mode 100644 index 0000000..5edcb8c --- /dev/null +++ b/src/main/java/com/qiwenshare/file/domain/FileType.java @@ -0,0 +1,26 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.domain; + +public enum FileType +{ + Word, + Cell, + Slide +} \ No newline at end of file diff --git a/src/main/java/com/qiwenshare/file/dto/file/CreateOfficeFileDTO.java b/src/main/java/com/qiwenshare/file/dto/file/CreateOfficeFileDTO.java new file mode 100644 index 0000000..f589886 --- /dev/null +++ b/src/main/java/com/qiwenshare/file/dto/file/CreateOfficeFileDTO.java @@ -0,0 +1,10 @@ +package com.qiwenshare.file.dto.file; + +import lombok.Data; + +@Data +public class CreateOfficeFileDTO { + private String filePath; + private String fileName; + private String extendName; +} diff --git a/src/main/java/com/qiwenshare/file/dto/file/EditOfficeFileDTO.java b/src/main/java/com/qiwenshare/file/dto/file/EditOfficeFileDTO.java new file mode 100644 index 0000000..45c93de --- /dev/null +++ b/src/main/java/com/qiwenshare/file/dto/file/EditOfficeFileDTO.java @@ -0,0 +1,12 @@ +package com.qiwenshare.file.dto.file; + +import lombok.Data; + +@Data +public class EditOfficeFileDTO { + private long userFileId; + private String previewUrl; +// private String filePath; +// private String fileName; +// private String extendName; +} diff --git a/src/main/java/com/qiwenshare/file/dto/file/OnlyofficeDTO.java b/src/main/java/com/qiwenshare/file/dto/file/OnlyofficeDTO.java new file mode 100644 index 0000000..f323fe3 --- /dev/null +++ b/src/main/java/com/qiwenshare/file/dto/file/OnlyofficeDTO.java @@ -0,0 +1,15 @@ +package com.qiwenshare.file.dto.file; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class OnlyofficeDTO { + private long fileId; + + private String fileName; + + private String fileUrl; + + private String extendName; +} diff --git a/src/main/java/com/qiwenshare/file/dto/file/PreviewOfficeFileDTO.java b/src/main/java/com/qiwenshare/file/dto/file/PreviewOfficeFileDTO.java new file mode 100644 index 0000000..72715e6 --- /dev/null +++ b/src/main/java/com/qiwenshare/file/dto/file/PreviewOfficeFileDTO.java @@ -0,0 +1,12 @@ +package com.qiwenshare.file.dto.file; + +import lombok.Data; + +@Data +public class PreviewOfficeFileDTO { + private long userFileId; + private String previewUrl; +// private String filePath; +// private String fileName; +// private String extendName; +} diff --git a/src/main/java/com/qiwenshare/file/helper/ConfigManager.java b/src/main/java/com/qiwenshare/file/helper/ConfigManager.java new file mode 100644 index 0000000..91cbe3c --- /dev/null +++ b/src/main/java/com/qiwenshare/file/helper/ConfigManager.java @@ -0,0 +1,61 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.helper; + +import org.springframework.stereotype.Component; + +import java.io.InputStream; +import java.util.Properties; +@Component +public class ConfigManager +{ + private static Properties properties; + + static + { + Init(); + } + + private static void Init() + { + try + { + properties = new Properties(); + InputStream stream = ConfigManager.class.getResourceAsStream("/config/settings.properties"); +// InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("settings.properties"); + properties.load(stream); + } + catch (Exception ex) + { + properties = null; + } + } + + public static String GetProperty(String name) + { + if (properties == null) + { + return ""; + } + + String property = properties.getProperty(name); + + return property == null ? "" : property; + } +} \ No newline at end of file diff --git a/src/main/java/com/qiwenshare/file/helper/DocumentManager.java b/src/main/java/com/qiwenshare/file/helper/DocumentManager.java new file mode 100644 index 0000000..60185ed --- /dev/null +++ b/src/main/java/com/qiwenshare/file/helper/DocumentManager.java @@ -0,0 +1,414 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.helper; + + + +import com.alibaba.fastjson.JSONObject; +import com.qiwenshare.file.domain.FileType; +import org.primeframework.jwt.Signer; +import org.primeframework.jwt.Verifier; +import org.primeframework.jwt.domain.JWT; +import org.primeframework.jwt.hmac.HMACSigner; +import org.primeframework.jwt.hmac.HMACVerifier; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.net.InetAddress; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.*; + +public class DocumentManager +{ + private static HttpServletRequest request; + + public static void Init(HttpServletRequest req, HttpServletResponse resp) + { + request = req; + } + + public static long GetMaxFileSize() + { + long size; + + try + { + size = Long.parseLong(ConfigManager.GetProperty("filesize-max")); + } + catch (Exception ex) + { + size = 0; + } + + return size > 0 ? size : 5 * 1024 * 1024; + } + + public static List GetFileExts() + { + List res = new ArrayList<>(); + + res.addAll(GetViewedExts()); + res.addAll(GetEditedExts()); + res.addAll(GetConvertExts()); + + return res; + } + + public static List GetViewedExts() + { + String exts = ConfigManager.GetProperty("files.docservice.viewed-docs"); + return Arrays.asList(exts.split("\\|")); + } + + public static List GetEditedExts() + { + String exts = ConfigManager.GetProperty("files.docservice.edited-docs"); + return Arrays.asList(exts.split("\\|")); + } + + public static List GetConvertExts() + { + String exts = ConfigManager.GetProperty("files.docservice.convert-docs"); + return Arrays.asList(exts.split("\\|")); + } + + public static String CurUserHostAddress(String userAddress) + { + if(userAddress == null) + { + try + { + userAddress = InetAddress.getLocalHost().getHostAddress(); + } + catch (Exception ex) + { + userAddress = ""; + } + } + + return userAddress.replaceAll("[^0-9a-zA-Z.=]", "_"); + } + + public static String FilesRootPath(String userAddress) + { + String hostAddress = CurUserHostAddress(userAddress); + String serverPath = request.getSession().getServletContext().getRealPath(""); + String storagePath = ConfigManager.GetProperty("storage-folder"); + String directory = serverPath + storagePath + File.separator + hostAddress + File.separator; + + File file = new File(directory); + + if (!file.exists()) + { + file.mkdirs(); + } + + return directory; + } + + public static String StoragePath(String fileName, String userAddress) + { + String directory = FilesRootPath(userAddress); + return directory + FileUtility.GetFileName(fileName); + } + + public static String ForcesavePath(String fileName, String userAddress, Boolean create) + { + String hostAddress = CurUserHostAddress(userAddress); + String serverPath = request.getSession().getServletContext().getRealPath(""); + String storagePath = ConfigManager.GetProperty("storage-folder"); + + String directory = serverPath + storagePath + File.separator + hostAddress + File.separator; + + File file = new File(directory); + if (!file.exists()) return ""; + + directory = directory + fileName + "-hist" + File.separator; + file = new File(directory); + if (!create && !file.exists()) return ""; + + file.mkdirs(); + + directory = directory + fileName; + file = new File(directory); + if (!create && !file.exists()) { + return ""; + } + + return directory; + } + + public static String HistoryDir(String storagePath) + { + return storagePath += "-hist"; + } + + public static String VersionDir(String histPath, Integer version) + { + return histPath + File.separator + Integer.toString(version); + } + + public static String VersionDir(String fileName, String userAddress, Integer version) + { + return VersionDir(HistoryDir(StoragePath(fileName, userAddress)), version); + } + + public static Integer GetFileVersion(String historyPath) + { + File dir = new File(historyPath); + + if (!dir.exists()) return 0; + + File[] dirs = dir.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isDirectory(); + } + }); + + return dirs.length + 1; + } + + public static int GetFileVersion(String fileName, String userAddress) + { + return GetFileVersion(HistoryDir(StoragePath(fileName, userAddress))); + } + + public static String GetCorrectName(String fileName, String userAddress) + { + String baseName = FileUtility.GetFileNameWithoutExtension(fileName); + String ext = FileUtility.GetFileExtension(fileName); + String name = baseName + ext; + + File file = new File(StoragePath(name, userAddress)); + + for (int i = 1; file.exists(); i++) + { + name = baseName + " (" + i + ")" + ext; + file = new File(StoragePath(name, userAddress)); + } + + return name; + } + + public static void CreateMeta(String fileName, String uid, String uname, String userAddress) throws Exception + { + String histDir = HistoryDir(StoragePath(fileName, userAddress)); + + File dir = new File(histDir); + dir.mkdir(); + + JSONObject json = new JSONObject(); + json.put("created", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + json.put("id", (uid == null || uid.isEmpty()) ? "uid-1" : uid); + if (json.get("id").equals("uid-0")) { + json.put("name", null); + } else { + json.put("name", (uname == null || uname.isEmpty()) ? "John Smith" : uname); + } + + File meta = new File(histDir + File.separator + "createdInfo.json"); + try (FileWriter writer = new FileWriter(meta)) { + json.writeJSONString(writer); + } + } + + public static File[] GetStoredFiles(String userAddress) + { + String directory = FilesRootPath(userAddress); + + File file = new File(directory); + return file.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isFile(); + } + }); + } + + public static String CreateDemo(String fileExt, Boolean sample, String uid, String uname) throws Exception + { + String demoName = (sample ? "sample." : "new.") + fileExt; + String demoPath = "assets" + File.separator + (sample ? "sample" : "new") + File.separator; + String fileName = GetCorrectName(demoName, null); + + InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(demoPath + demoName); + + File file = new File(StoragePath(fileName, null)); + + try (FileOutputStream out = new FileOutputStream(file)) + { + int read; + final byte[] bytes = new byte[1024]; + while ((read = stream.read(bytes)) != -1) + { + out.write(bytes, 0, read); + } + out.flush(); + } + + CreateMeta(fileName, uid, uname, null); + + return fileName; + } + + public static String GetFileUri(String fileName, Boolean forDocumentServer) + { + try + { + String serverPath = GetServerUrl(forDocumentServer); + String storagePath = ConfigManager.GetProperty("storage-folder"); + String hostAddress = CurUserHostAddress(null); + + String filePath = serverPath + "/" + storagePath + "/" + hostAddress + "/" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()).replace("+", "%20"); + + return filePath; + } + catch (UnsupportedEncodingException e) + { + return ""; + } + } + + public static ArrayList> GetFilesInfo(){ + ArrayList> files = new ArrayList<>(); + + for(File file : GetStoredFiles(null)){ + Map map = new LinkedHashMap<>(); + map.put("version", GetFileVersion(file.getName(), null)); + map.put("id", ServiceConverter.GenerateRevisionId(CurUserHostAddress(null) + "/" + file.getName() + "/" + Long.toString(new File(StoragePath(file.getName(), null)).lastModified()))); + map.put("contentLength", new BigDecimal(String.valueOf((file.length()/1024.0))).setScale(2, RoundingMode.HALF_UP) + " KB"); + map.put("pureContentLength", file.length()); + map.put("title", file.getName()); + map.put("updated", String.valueOf(new Date(file.lastModified()))); + files.add(map); + } + + return files; + } + + public static ArrayList> GetFilesInfo(String fileId){ + ArrayList> file = new ArrayList<>(); + + for (Map map : GetFilesInfo()){ + if (map.get("id").equals(fileId)){ + file.add(map); + break; + } + } + + return file; + } + + public static String GetPathUri(String path) + { + String serverPath = GetServerUrl(true); + String storagePath = ConfigManager.GetProperty("storage-folder"); + String hostAddress = CurUserHostAddress(null); + + String filePath = serverPath + "/" + storagePath + "/" + hostAddress + "/" + path.replace(File.separator, "/").substring(FilesRootPath(null).length()).replace(" ", "%20"); + + return filePath; + } + + + public static String GetServerUrl(Boolean forDocumentServer) { + if (forDocumentServer && !ConfigManager.GetProperty("files.docservice.url.example").equals("")) { + return ConfigManager.GetProperty("files.docservice.url.example"); + } else { + return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); + } + } + + public static String GetCallback(String fileName) + { + String serverPath = GetServerUrl(true); + String hostAddress = CurUserHostAddress(null); + try + { + String query = "?type=track&fileName=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()) + "&userAddress=" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString()); + + return serverPath + "/IndexServlet" + query; + } + catch (UnsupportedEncodingException e) + { + return ""; + } + } + + public static String GetInternalExtension(FileType fileType) + { + if (fileType.equals(FileType.Word)) + return ".docx"; + + if (fileType.equals(FileType.Cell)) + return ".xlsx"; + + if (fileType.equals(FileType.Slide)) + return ".pptx"; + + return ".docx"; + } + + public static String CreateToken(Map payloadClaims) + { + try + { + Signer signer = HMACSigner.newSHA256Signer(GetTokenSecret()); + JWT jwt = new JWT(); + for (String key : payloadClaims.keySet()) + { + jwt.addClaim(key, payloadClaims.get(key)); + } + return JWT.getEncoder().encode(jwt, signer); + } + catch (Exception e) + { + return ""; + } + } + + public static JWT ReadToken(String token) + { + try + { + Verifier verifier = HMACVerifier.newVerifier(GetTokenSecret()); + return JWT.getDecoder().decode(token, verifier); + } + catch (Exception exception) + { + return null; + } + } + + public static Boolean TokenEnabled() + { + String secret = GetTokenSecret(); + return secret != null && !secret.isEmpty(); + } + + private static String GetTokenSecret() + { + return ConfigManager.GetProperty("files.docservice.secret"); + } +} \ No newline at end of file diff --git a/src/main/java/com/qiwenshare/file/helper/FileUtility.java b/src/main/java/com/qiwenshare/file/helper/FileUtility.java new file mode 100644 index 0000000..fd6cf36 --- /dev/null +++ b/src/main/java/com/qiwenshare/file/helper/FileUtility.java @@ -0,0 +1,121 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.helper; + + + +import com.qiwenshare.file.domain.FileType; + +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FileUtility +{ + static {} + + public static FileType GetFileType(String fileName) + { + String ext = GetFileExtension(fileName).toLowerCase(); + + if (ExtsDocument.contains(ext)) + return FileType.Word; + + if (ExtsSpreadsheet.contains(ext)) + return FileType.Cell; + + if (ExtsPresentation.contains(ext)) + return FileType.Slide; + + return FileType.Word; + } + + public static List ExtsDocument = Arrays.asList + ( + ".doc", ".docx", ".docm", + ".dot", ".dotx", ".dotm", + ".odt", ".fodt", ".ott", ".rtf", ".txt", + ".html", ".htm", ".mht", ".xml", + ".pdf", ".djvu", ".fb2", ".epub", ".xps" + ); + + public static List ExtsSpreadsheet = Arrays.asList + ( + ".xls", ".xlsx", ".xlsm", + ".xlt", ".xltx", ".xltm", + ".ods", ".fods", ".ots", ".csv" + ); + + public static List ExtsPresentation = Arrays.asList + ( + ".pps", ".ppsx", ".ppsm", + ".ppt", ".pptx", ".pptm", + ".pot", ".potx", ".potm", + ".odp", ".fodp", ".otp" + ); + + + public static String GetFileName(String url) + { + if (url == null) return ""; + + String fileName = url.substring(url.lastIndexOf('/') + 1, url.length()); + fileName = fileName.split("\\?")[0]; + return fileName; + } + + public static String GetFileNameWithoutExtension(String url) + { + String fileName = GetFileName(url); + if (fileName == null) return null; + String fileNameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.')); + return fileNameWithoutExt; + } + + public static String GetFileExtension(String url) + { + String fileName = GetFileName(url); + if (fileName == null) return null; + String fileExt = fileName.substring(fileName.lastIndexOf(".")); + return fileExt.toLowerCase(); + } + + public static Map GetUrlParams(String url) + { + try + { + String query = new URL(url).getQuery(); + String[] params = query.split("&"); + Map map = new HashMap<>(); + for (String param : params) + { + String name = param.split("=")[0]; + String value = param.split("=")[1]; + map.put(name, value); + } + return map; + } + catch (Exception ex) + { + return null; + } + } +} diff --git a/src/main/java/com/qiwenshare/file/helper/ServiceConverter.java b/src/main/java/com/qiwenshare/file/helper/ServiceConverter.java new file mode 100644 index 0000000..f73c48a --- /dev/null +++ b/src/main/java/com/qiwenshare/file/helper/ServiceConverter.java @@ -0,0 +1,242 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.helper; + +import com.alibaba.fastjson.JSONObject; +import com.google.gson.Gson; +import java.io.*; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + + +public class ServiceConverter +{ + private static int ConvertTimeout = 120000; + private static final String DocumentConverterUrl = ConfigManager.GetProperty("files.docservice.url.site") + ConfigManager.GetProperty("files.docservice.url.converter"); + private static final String DocumentJwtHeader = ConfigManager.GetProperty("files.docservice.header"); + + public static class ConvertBody + { + public String url; + public String outputtype; + public String filetype; + public String title; + public String key; + public Boolean async; + public String token; + public String password; + } + + static + { + try + { + int timeout = Integer.parseInt(ConfigManager.GetProperty("files.docservice.timeout")); + if (timeout > 0) + { + ConvertTimeout = timeout; + } + } + catch (Exception ex) + { + } + } + + public static String GetConvertedUri(String documentUri, String fromExtension, String toExtension, String documentRevisionId, String filePass, Boolean isAsync) throws Exception + { + fromExtension = fromExtension == null || fromExtension.isEmpty() ? FileUtility.GetFileExtension(documentUri) : fromExtension; + + String title = FileUtility.GetFileName(documentUri); + title = title == null || title.isEmpty() ? UUID.randomUUID().toString() : title; + + documentRevisionId = documentRevisionId == null || documentRevisionId.isEmpty() ? documentUri : documentRevisionId; + + documentRevisionId = GenerateRevisionId(documentRevisionId); + + ConvertBody body = new ConvertBody(); + body.url = documentUri; + body.outputtype = toExtension.replace(".", ""); + body.filetype = fromExtension.replace(".", ""); + body.title = title; + body.key = documentRevisionId; + body.password = filePass; + if (isAsync) + body.async = true; + + String headerToken = ""; + if (DocumentManager.TokenEnabled()) + { + HashMap map = new HashMap(); + map.put("url", body.url); + map.put("outputtype", body.outputtype); + map.put("filetype", body.filetype); + map.put("title", body.title); + map.put("key", body.key); + map.put("password", body.password); + if (isAsync) + map.put("async", body.async); + + String token = DocumentManager.CreateToken(map); + body.token = token; + + Map payloadMap = new HashMap(); + payloadMap.put("payload", map); + headerToken = DocumentManager.CreateToken(payloadMap); + } + + Gson gson = new Gson(); + String bodyString = gson.toJson(body); + + byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8); + + URL url = new URL(DocumentConverterUrl); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setFixedLengthStreamingMode(bodyByte.length); + connection.setRequestProperty("Accept", "application/json"); + connection.setConnectTimeout(ConvertTimeout); + + if (DocumentManager.TokenEnabled()) + { + connection.setRequestProperty(DocumentJwtHeader.equals("") ? "Authorization" : DocumentJwtHeader, "Bearer " + headerToken); + } + + connection.connect(); + try (OutputStream os = connection.getOutputStream()) { + os.write(bodyByte); + } + + InputStream stream = connection.getInputStream(); + + if (stream == null) + throw new Exception("Could not get an answer"); + + String jsonString = ConvertStreamToString(stream); + + connection.disconnect(); + + return GetResponseUri(jsonString); + } + + public static String GenerateRevisionId(String expectedKey) + { + if (expectedKey.length() > 20) + expectedKey = Integer.toString(expectedKey.hashCode()); + + String key = expectedKey.replace("[^0-9-.a-zA-Z_=]", "_"); + + return key.substring(0, Math.min(key.length(), 20)); + } + + private static void ProcessConvertServiceResponceError(int errorCode) throws Exception + { + String errorMessage = ""; + String errorMessageTemplate = "Error occurred in the ConvertService: "; + + switch (errorCode) + { + case -8: + errorMessage = errorMessageTemplate + "Error document VKey"; + break; + case -7: + errorMessage = errorMessageTemplate + "Error document request"; + break; + case -6: + errorMessage = errorMessageTemplate + "Error database"; + break; + case -5: + errorMessage = errorMessageTemplate + "Incorrect password"; + break; + case -4: + errorMessage = errorMessageTemplate + "Error download error"; + break; + case -3: + errorMessage = errorMessageTemplate + "Error convertation error"; + break; + case -2: + errorMessage = errorMessageTemplate + "Error convertation timeout"; + break; + case -1: + errorMessage = errorMessageTemplate + "Error convertation unknown"; + break; + case 0: + break; + default: + errorMessage = "ErrorCode = " + errorCode; + break; + } + + throw new Exception(errorMessage); + } + + private static String GetResponseUri(String jsonString) throws Exception + { + JSONObject jsonObj = ConvertStringToJSON(jsonString); + + Object error = jsonObj.get("error"); + if (error != null) + ProcessConvertServiceResponceError(Math.toIntExact((long)error)); + + Boolean isEndConvert = (Boolean) jsonObj.get("endConvert"); + + Long resultPercent = 0l; + String responseUri = null; + + if (isEndConvert) + { + resultPercent = 100l; + responseUri = (String) jsonObj.get("fileUrl"); + } + else + { + resultPercent = (Long) jsonObj.get("percent"); + resultPercent = resultPercent >= 100l ? 99l : resultPercent; + } + + return resultPercent >= 100l ? responseUri : ""; + } + + public static String ConvertStreamToString(InputStream stream) throws IOException + { + InputStreamReader inputStreamReader = new InputStreamReader(stream); + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + String line = bufferedReader.readLine(); + + while (line != null) + { + stringBuilder.append(line); + line = bufferedReader.readLine(); + } + + String result = stringBuilder.toString(); + + return result; + } + + public static JSONObject ConvertStringToJSON(String jsonString) + { + return JSONObject.parseObject(jsonString); + } +} \ No newline at end of file diff --git a/src/main/java/com/qiwenshare/file/helper/TrackManager.java b/src/main/java/com/qiwenshare/file/helper/TrackManager.java new file mode 100644 index 0000000..e4edf2f --- /dev/null +++ b/src/main/java/com/qiwenshare/file/helper/TrackManager.java @@ -0,0 +1,301 @@ +/** + * + * (c) Copyright Ascensio System SIA 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.qiwenshare.file.helper; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.gson.Gson; +import org.primeframework.jwt.domain.JWT; + +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Scanner; + +public class TrackManager { + private static final String DocumentJwtHeader = ConfigManager.GetProperty("files.docservice.header"); + + public static JSONObject readBody(HttpServletRequest request, PrintWriter writer) throws Exception { + String bodyString = ""; + + try { + Scanner scanner = new Scanner(request.getInputStream()); + scanner.useDelimiter("\\A"); + bodyString = scanner.hasNext() ? scanner.next() : ""; + scanner.close(); + } + catch (Exception ex) { + writer.write("get request.getInputStream error:" + ex.getMessage()); + throw ex; + } + + if (bodyString.isEmpty()) { + writer.write("empty request.getInputStream"); + throw new Exception("empty request.getInputStream"); + } + + JSONObject body; + + try { + body = JSONObject.parseObject(bodyString); + } catch (Exception ex) { + writer.write("JSONParser.parse error:" + ex.getMessage()); + throw ex; + } + + if (DocumentManager.TokenEnabled()) { + String token = (String) body.get("token"); + + if (token == null) { + String header = (String) request.getHeader(DocumentJwtHeader == null || DocumentJwtHeader.isEmpty() ? "Authorization" : DocumentJwtHeader); + if (header != null && !header.isEmpty()) { + token = header.startsWith("Bearer ") ? header.substring(7) : header; + } + } + + if (token == null || token.isEmpty()) { + writer.write("{\"error\":1,\"message\":\"JWT expected\"}"); + throw new Exception("{\"error\":1,\"message\":\"JWT expected\"}"); + } + + JWT jwt = DocumentManager.ReadToken(token); + if (jwt == null) { + writer.write("{\"error\":1,\"message\":\"JWT validation failed\"}"); + throw new Exception("{\"error\":1,\"message\":\"JWT validation failed\"}"); + } + + if (jwt.getObject("payload") != null) { + try { + @SuppressWarnings("unchecked") LinkedHashMap payload = + (LinkedHashMap)jwt.getObject("payload"); + + jwt.claims = payload; + } catch (Exception ex) { + writer.write("{\"error\":1,\"message\":\"Wrong payload\"}"); + throw ex; + } + } + + try { + Gson gson = new Gson(); + body = JSONObject.parseObject(gson.toJson(jwt.claims)); + } catch (Exception ex) { + writer.write("JSONParser.parse error:" + ex.getMessage()); + throw ex; + } + } + + return body; + } + + public static void processSave(JSONObject body, String fileName, String userAddress) throws Exception { + String downloadUri = (String) body.get("url"); + String changesUri = (String) body.get("changesurl"); + String key = (String) body.get("key"); + String newFileName = fileName; + + String curExt = FileUtility.GetFileExtension(fileName); + String downloadExt = FileUtility.GetFileExtension(downloadUri); + + if (!curExt.equals(downloadExt)) { + try { + String newFileUri = ServiceConverter.GetConvertedUri(downloadUri, downloadExt, curExt, ServiceConverter.GenerateRevisionId(downloadUri), null, false); + if (newFileUri.isEmpty()) { + newFileName = DocumentManager.GetCorrectName(FileUtility.GetFileNameWithoutExtension(fileName) + downloadExt, userAddress); + } else { + downloadUri = newFileUri; + } + } catch (Exception e){ + newFileName = DocumentManager.GetCorrectName(FileUtility.GetFileNameWithoutExtension(fileName) + downloadExt, userAddress); + } + } + + String storagePath = DocumentManager.StoragePath(newFileName, userAddress); + File histDir = new File(DocumentManager.HistoryDir(storagePath)); + if (!histDir.exists()) histDir.mkdirs(); + + String versionDir = DocumentManager.VersionDir(histDir.getAbsolutePath(), DocumentManager.GetFileVersion(histDir.getAbsolutePath())); + File ver = new File(versionDir); + File lastVersion = new File(DocumentManager.StoragePath(fileName, userAddress)); + File toSave = new File(storagePath); + + if (!ver.exists()) ver.mkdirs(); + + lastVersion.renameTo(new File(versionDir + File.separator + "prev" + curExt)); + + downloadToFile(downloadUri, toSave); + downloadToFile(changesUri, new File(versionDir + File.separator + "diff.zip")); + + String history = (String) body.get("changeshistory"); + if (history == null && body.containsKey("history")) { + history = ((JSONObject) body.get("history")).toJSONString(); + } + if (history != null && !history.isEmpty()) { + FileWriter fw = new FileWriter(new File(versionDir + File.separator + "changes.json")); + fw.write(history); + fw.close(); + } + + FileWriter fw = new FileWriter(new File(versionDir + File.separator + "key.txt")); + fw.write(key); + fw.close(); + + String forcesavePath = DocumentManager.ForcesavePath(newFileName, userAddress, false); + if (!forcesavePath.equals("")) { + File forceSaveFile = new File(forcesavePath); + forceSaveFile.delete(); + } + } + + public static void processForceSave(JSONObject body, String fileName, String userAddress) throws Exception { + + String downloadUri = (String) body.get("url"); + + String curExt = FileUtility.GetFileExtension(fileName); + String downloadExt = FileUtility.GetFileExtension(downloadUri); + Boolean newFileName = false; + + if (!curExt.equals(downloadExt)) { + try { + String newFileUri = ServiceConverter.GetConvertedUri(downloadUri, downloadExt, curExt, ServiceConverter.GenerateRevisionId(downloadUri), null, false); + if (newFileUri.isEmpty()) { + newFileName = true; + } else { + downloadUri = newFileUri; + } + } catch (Exception e){ + newFileName = true; + } + } + + String forcesavePath = ""; + boolean isSubmitForm = body.get("forcesavetype").toString().equals("3"); + + if (isSubmitForm) { + //new file + if (newFileName){ + fileName = DocumentManager.GetCorrectName(FileUtility.GetFileNameWithoutExtension(fileName) + "-form" + downloadExt, userAddress); + } else { + fileName = DocumentManager.GetCorrectName(FileUtility.GetFileNameWithoutExtension(fileName) + "-form" + curExt, userAddress); + } + forcesavePath = DocumentManager.StoragePath(fileName, userAddress); + } else { + if (newFileName){ + fileName = DocumentManager.GetCorrectName(FileUtility.GetFileNameWithoutExtension(fileName) + downloadExt, userAddress); + } + + forcesavePath = DocumentManager.ForcesavePath(fileName, userAddress, false); + if (forcesavePath == "") { + forcesavePath = DocumentManager.ForcesavePath(fileName, userAddress, true); + } + } + + File toSave = new File(forcesavePath); + downloadToFile(downloadUri, toSave); + + if (isSubmitForm) { + JSONArray actions = (JSONArray) body.get("actions"); + JSONObject action = (JSONObject) actions.get(0); + String user = (String) action.get("userid"); + DocumentManager.CreateMeta(fileName, user, "Filling Form", userAddress); + } + } + + private static void downloadToFile(String url, File file) throws Exception { + if (url == null || url.isEmpty()) throw new Exception("argument url"); + if (file == null) throw new Exception("argument path"); + + URL uri = new URL(url); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) uri.openConnection(); + InputStream stream = connection.getInputStream(); + + if (stream == null) + { + throw new Exception("Stream is null"); + } + + try (FileOutputStream out = new FileOutputStream(file)) + { + int read; + final byte[] bytes = new byte[1024]; + while ((read = stream.read(bytes)) != -1) + { + out.write(bytes, 0, read); + } + + out.flush(); + } + + connection.disconnect(); + } + + public static void commandRequest(String method, String key) throws Exception { + String DocumentCommandUrl = ConfigManager.GetProperty("files.docservice.url.site") + ConfigManager.GetProperty("files.docservice.url.command"); + + URL url = new URL(DocumentCommandUrl); + java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); + + HashMap params = new HashMap(); + params.put("c", method); + params.put("key", key); + + String headerToken = ""; + if (DocumentManager.TokenEnabled()) + { + Map payloadMap = new HashMap(); + payloadMap.put("payload", params); + headerToken = DocumentManager.CreateToken(payloadMap); + + connection.setRequestProperty(DocumentJwtHeader.equals("") ? "Authorization" : DocumentJwtHeader, "Bearer " + headerToken); + + String token = DocumentManager.CreateToken(params); + params.put("token", token); + } + + Gson gson = new Gson(); + String bodyString = gson.toJson(params); + + byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8); + + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setDoOutput(true); + + connection.connect(); + try (OutputStream os = connection.getOutputStream()) { + os.write(bodyByte); + } + InputStream stream = connection.getInputStream();; + + if (stream == null) + throw new Exception("Could not get an answer"); + + String jsonString = ServiceConverter.ConvertStreamToString(stream); + connection.disconnect(); + + JSONObject response = ServiceConverter.ConvertStringToJSON(jsonString); + if (!response.get("error").toString().equals("0")){ + throw new Exception(response.toJSONString()); + } + } +} diff --git a/src/main/java/com/qiwenshare/file/mapper/UserFileMapper.java b/src/main/java/com/qiwenshare/file/mapper/UserFileMapper.java index 0d4b3b4..b0fc3f5 100644 --- a/src/main/java/com/qiwenshare/file/mapper/UserFileMapper.java +++ b/src/main/java/com/qiwenshare/file/mapper/UserFileMapper.java @@ -12,7 +12,7 @@ import java.util.Map; public interface UserFileMapper extends BaseMapper { void replaceFilePath(@Param("filePath") String filePath, @Param("oldFilePath") String oldFilePath, @Param("userId") Long userId); - List userFileList(UserFile userFile, Long beginCount, Long pageCount); + List userFileList(@Param("userFile") UserFile userFile, Long beginCount, Long pageCount); void updateFilepathByPathAndName(String oldfilePath, String newfilePath, String fileName, String extendName, long userId); void updateFilepathByFilepath(String oldfilePath, String newfilePath, long userId); diff --git a/src/main/java/com/qiwenshare/file/service/UserFileService.java b/src/main/java/com/qiwenshare/file/service/UserFileService.java index 67a9427..eff3ab2 100644 --- a/src/main/java/com/qiwenshare/file/service/UserFileService.java +++ b/src/main/java/com/qiwenshare/file/service/UserFileService.java @@ -47,6 +47,17 @@ public class UserFileService extends ServiceImpl impl return userFileMapper.selectList(lambdaQueryWrapper); } + @Override + public List selectSameUserFile(String fileName, String filePath, String extendName, Long userId) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(UserFile::getFileName, fileName) + .eq(UserFile::getFilePath, filePath) + .eq(UserFile::getUserId, userId) + .eq(UserFile::getExtendName, extendName) + .eq(UserFile::getDeleteFlag, "0"); + return userFileMapper.selectList(lambdaQueryWrapper); + } + @Override public void replaceUserFilePath(String filePath, String oldFilePath, Long userId) { userFileMapper.replaceFilePath(filePath, oldFilePath, userId); diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index c147bc5..4ca4d35 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -93,3 +93,8 @@ spring.redis.timeout=5000 spring.data.elasticsearch.client.reactive.endpoints=127.0.0.1:9200 spring.elasticsearch.rest.uris=127.0.0.1:9200 + +# 当前部署外网IP,用于office预览 +deployment.host: 172.17.242.97:${server.port} + + diff --git a/src/main/resources/config/settings.properties b/src/main/resources/config/settings.properties new file mode 100644 index 0000000..7f91898 --- /dev/null +++ b/src/main/resources/config/settings.properties @@ -0,0 +1,18 @@ +filesize-max=5242880 +storage-folder=app_data + +files.docservice.viewed-docs=.pdf|.djvu|.xps +files.docservice.edited-docs=.docx|.xlsx|.csv|.pptx|.txt +files.docservice.convert-docs=.docm|.dotx|.dotm|.dot|.doc|.odt|.fodt|.ott|.xlsm|.xltx|.xltm|.xlt|.xls|.ods|.fods|.ots|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.rtf|.mht|.html|.htm|.xml|.epub|.fb2 +files.docservice.timeout=120000 + +files.docservice.url.site=http://172.17.242.97:80/ +files.docservice.url.converter=ConvertService.ashx +files.docservice.url.command=coauthoring/CommandService.ashx +files.docservice.url.api=web-apps/apps/api/documents/api.js +files.docservice.url.preloader=web-apps/apps/api/documents/cache-scripts.html +files.docservice.url.example= + +#files.docservice.plugins=../../../../sdkjs-plugins/sdkjs-plugins-auto/textlibrary/config.json,../../../../sdkjs-plugins/sdkjs-plugins-auto/clipart/config.json +files.docservice.secret= +files.docservice.header=Authorization \ No newline at end of file