其实这个有很多办法可以实现。
利用office的在线预览,vue的依赖,转换成pdf进行预览。三个办法我都试了。微软官方的office在线预览的话需要一个公网ip,同时有的时候还需要你科学上网,这个直接寄。
word官方预览链接(支持三种格式,部分word带特殊符号或流程图无法显示)
1 2 3
| const routeUrl = file.url; // 文件路径 const url = encodeURIComponent(routeUrl); const officeUrl = "https://view.xdocin.com/view?src=" + url;
|
office官方预览(pdf不能展示)
1
| let officeUrl = 'http://view.officeapps.live.com/op/view.aspx?src='+url
|
vue的依赖的话,GitHub上搜索一下会有很多。vue-office这个项目很棒,可惜我引入包和标签的时候会报错,人家用的是js,而我用的是ts,这个也pass了。这个是项目地址,非常棒的一个项目,可以参考学习一下:https://github.com/501351981/vue-office
到最后,还要回到word转换成office的破办法,这是我最难接受的一个,用的人一多,后端压力就大了,万一流量上来了,服务器直接开摆,笑死。但是实在是没办法了,退退退而求其次,只能这样了。主要思路是前端把文件id获取到,去数据库搜索到存放word的url地址,后端通过url地址把doc文档转换成输入流,然后通过输出流存放到一个专属的pdf文件夹,再把这个pdf生成链接给前端,前端显示的话就用这一个链接进行显示。
前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| <template> <el-dialog v-model="visible" :title="$t('文件预览')" :close-on-click-modal="false" :close-on-press-escape="false"> <div v-if="fileMessage.fileType == 'jpg' || fileMessage.fileType == 'png' || fileMessage.fileType == 'ico' || fileMessage.fileType == 'gif' || fileMessage.fileType == 'webp' "> <el-image style="width: 100%" :src="fileMessage.getFilePath"> </el-image> </div> <div v-else-if="fileMessage.fileType == 'docx' || fileMessage.fileType == 'doc'"> <embed :src="result.url" style="width: 100%; height: 600px" /> <!-- <h2 style="text-align: center;color: brown; ">暂不支持doc、docx格式!</h2> --> </div> <div v-else-if="fileMessage.fileType == 'xlsx' || fileMessage.fileType == 'xls'"> <h2 style="text-align: center;color: brown; ">暂不支持该xlsx、xls格式!</h2> </div> <div v-else-if="fileMessage.fileType == 'pdf'"> <embed :src="fileMessage.getFilePath" style="width: 100%; height: 600px" /> </div> <div v-else> <h2 style="text-align: center;color: brown; ">暂不支持该文件格式!</h2> </div> <template v-slot:footer> <el-button @click="visible = false">{{ $t("退出预览") }}</el-button> </template> </el-dialog> </template> <script lang="ts" setup> import { reactive, ref } from "vue"; import baseService from "@/service/baseService"; import { ElMessage } from "element-plus";
const visible = ref(false); const dataFormRef = ref();
const fileMessage = reactive({ getFilePath: "", fileType: "" });
const dataForm = reactive({ uploadFileId: "", uploadCompanyName: "", uploadFileName: "", uploadFilePath: "", uploadUserName: "", uploadFileTime: "", });
const init = (uploadFileId?: number) => { visible.value = true; dataForm.uploadFileId = "";
//重置表单数据 if (dataFormRef.value) { dataFormRef.value.resetFields(); }
if (uploadFileId) { getInfo(uploadFileId); } };
const result = reactive({ url: "", msg: "" }); //var showType: string; // 获取信息 const getInfo = (uploadFileId: number) => { baseService.get("/uploadFile/xyjqfileupload/" + uploadFileId).then((res) => { Object.assign(dataForm, res.data); if (dataForm.uploadFilePath != "") { fileMessage.getFilePath = dataForm.uploadFilePath; //获取到图片的路径,写在这里面才管用,写在外面,第一次请求时候没有数据 //console.log(getFilePath.substring(getFilePath.lastIndexOf(".") + 1, getFilePath.length)); //console.log(getFilePath); fileMessage.fileType = fileMessage.getFilePath.substring(fileMessage.getFilePath.lastIndexOf(".") + 1, fileMessage.getFilePath.length); //showType = fileType; console.log("文件类型:" + fileMessage.fileType); console.log(fileMessage.getFilePath); if (fileMessage.fileType == "doc" || fileMessage.fileType == "docx") { baseService .post(`/uploadFile/xyjqfileupload/pdfUrl/${uploadFileId}`) .then((res) => { if (res.code === 0) { result.url = res.data; console.log(res.data); } else { ElMessage.error(res.msg); result.msg = res.msg; } }) .catch((err) => { ElMessage.error(err.message); }); } } else { ElMessage.error("链接地址为空,无法打开"); } }); };
defineExpose({ init }); </script>
|
主要核心代码是这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const getInfo = (uploadFileId: number) => { baseService.get("/uploadFile/xyjqfileupload/" + uploadFileId).then((res) => { Object.assign(dataForm, res.data); if (dataForm.uploadFilePath != "") { fileMessage.getFilePath = dataForm.uploadFilePath; fileMessage.fileType = fileMessage.getFilePath.substring(fileMessage.getFilePath.lastIndexOf(".") + 1, fileMessage.getFilePath.length); console.log("文件类型:" + fileMessage.fileType); console.log(fileMessage.getFilePath); if (fileMessage.fileType == "doc" || fileMessage.fileType == "docx") { baseService .post(`/uploadFile/xyjqfileupload/pdfUrl/${uploadFileId}`) .then((res) => { if (res.code === 0) { result.url = res.data; console.log(res.data); } else { ElMessage.error(res.msg); result.msg = res.msg; } }) .catch((err) => { ElMessage.error(err.message); }); } } else { ElMessage.error("链接地址为空,无法打开"); } }); };
|
主要就是获取到文件之后看看文件后缀,欸!是doc或者docx格式的,那就继续post后端,把文件id发过去,这样后端就能通过id在数据库里面找到文件的在线链接。然后进行下载解析word。
后端部分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @PostMapping("/pdfUrl/{fileId}") @ApiOperation("WORD转换PdF") @LogOperation("WORD转换pdf") public Result wordToPdf(@PathVariable("fileId") String fileId) throws Exception { XyjqFileUploadEntity xyjqFileUploadEntity = xyjqFileUploadService.selectById(fileId); String url = xyjqFileUploadEntity.getUploadFilePath(); String strFileNameAndPoint = url.substring(url.lastIndexOf("/")+1); String newFileName = strFileNameAndPoint.substring(0,strFileNameAndPoint.lastIndexOf("."))+"_pdf.pdf"; System.out.println(newFileName); String paths = "D:\\Archive\\Desktop\\uploadFile\\WordToPdf\\"+newFileName; File outputFile = new File(paths);
URL receiveUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection)receiveUrl.openConnection(); conn.setConnectTimeout(3*1000); InputStream docxInputStream = conn.getInputStream(); try { OutputStream outputStream = new FileOutputStream(outputFile); IConverter converter = LocalConverter.builder().build(); converter. convert(docxInputStream).as(DocumentType.DOCX). to(outputStream).as(DocumentType.PDF). execute(); outputStream.close(); System.out.println("Word To Pdf 转换完成"); } catch (Exception e) { e.printStackTrace(); }
File file = new File(paths); MultipartFile cMultiFile = getMultipartFile(file); String newUrl = OSSFactory.build().uploadSuffix(cMultiFile.getBytes(), "pdf"); System.out.println(newUrl); return new Result().ok(newUrl); } public static MultipartFile getMultipartFile(File file) { DiskFileItem item = new DiskFileItem("file" , MediaType.MULTIPART_FORM_DATA_VALUE , true , file.getName() , (int)file.length() , file.getParentFile()); try { OutputStream os = item.getOutputStream(); os.write(FileUtils.readFileToByteArray(file)); } catch (IOException e) { e.printStackTrace(); } return new CommonsMultipartFile(item); }
|
我开始是想传输url的,但是post之后,发现失败,原来前端不承认这样的链接啊,我也是太天真了,所以只能这么做了。
哦对,还需要引入两个依赖
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>com.documents4j</groupId> <artifactId>documents4j-local</artifactId> <version>1.0.3</version> </dependency> <dependency> <groupId>com.documents4j</groupId> <artifactId>documents4j-transformer-msoffice-word</artifactId> <version>1.0.3</version> </dependency>
|
基本情况是这个样子,中间还少了一些判断,比如第二次打开的时候,这个链接其实以及生成一个pdf了,再次预览的话,又要生成一个一样的就覆盖了,实在是太占用资源了,还不如去mysql里面做个表去保存这个生成的预览pdf链接,然后直接返还,这样就能再一定程度节省服务器资源了。目前没写主要是太困了,我需要睡觉了。。。想起来再写吧。