简单的网站都是一套的逻辑爬虫,大家可以参考一下。有兴趣的话帮忙点个start支持一下
前后端系统以及数据库
https://gitee.com/wonder19991209/mohurd\_search\_sys
爬虫脚本
]]>利用office的在线预览,vue的依赖,转换成pdf进行预览。三个办法我都试了。微软官方的office在线预览的话需要一个公网ip,同时有的时候还需要你科学上网,这个直接寄。
word官方预览链接(支持三种格式,部分word带特殊符号或流程图无法显示)
const routeUrl = file.url; // 文件路径
const url = encodeURIComponent(routeUrl);
const officeUrl = "https://view.xdocin.com/view?src=" + url;
office官方预览(pdf不能展示)
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生成链接给前端,前端显示的话就用这一个链接进行显示。
前端代码
<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>
主要核心代码是这里
// 获取信息
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("链接地址为空,无法打开");
}
});
};
主要就是获取到文件之后看看文件后缀,欸!是doc或者docx格式的,那就继续post后端,把文件id发过去,这样后端就能通过id在数据库里面找到文件的在线链接。然后进行下载解析word。
后端部分代码
@PostMapping("/pdfUrl/{fileId}")
@ApiOperation("WORD转换PdF")
@LogOperation("WORD转换pdf")
public Result wordToPdf(@PathVariable("fileId") String fileId) throws Exception {
XyjqFileUploadEntity xyjqFileUploadEntity = xyjqFileUploadService.selectById(fileId);
//获取到url地址
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;
//先提前构建一个生成的pdf保存路径
File outputFile = new File(paths);
//先把获取到的文件转换成输入流的模式
URL receiveUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)receiveUrl.openConnection();
//设置超时间为3秒
conn.setConnectTimeout(3*1000);
//将传输过来的doc文件转换成流
InputStream docxInputStream = conn.getInputStream();
try {
OutputStream outputStream = new FileOutputStream(outputFile);
IConverter converter = LocalConverter.builder().build();
converter.
convert(docxInputStream).as(DocumentType.DOCX).//作为doc格式
to(outputStream).as(DocumentType.PDF).
execute();
outputStream.close();
System.out.println("Word To Pdf 转换完成");
} catch (Exception e) {
e.printStackTrace();
}
//将file格式转换成MultipartFile格式
File file = new File(paths);
MultipartFile cMultiFile = getMultipartFile(file);
//获取到新的url返还给前端进行预览
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之后,发现失败,原来前端不承认这样的链接啊,我也是太天真了,所以只能这么做了。
哦对,还需要引入两个依赖
<!-- 转换成PDF-->
<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链接,然后直接返还,这样就能再一定程度节省服务器资源了。目前没写主要是太困了,我需要睡觉了。。。想起来再写吧。
]]>==
用于比较两者是否相等,忽略数据类型。===
用于更严谨的比较,值和值的数据类型都需要同时比较。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script src="vue.js" type="text/javascript" charset="utf-8"></script>
<body>
<div id="app">
<div v-if="text ==1 ">
1--值一样,类型不同,两个等号显示
</div>
<div v-if="text ===1 ">
2--值一样,类型不同,三个等号不显示
</div>
<div v-if="text ==='1' ">
3--值一样,类型同,三个等号显示
</div>
</div>
<script type="text/javascript">
/* text:"1" 这里的1是个字符串 */
var vm = new Vue({
el:"#app",
data:{
text:"1"
}
});
</script>
</body>
</html>
显示:
1--值一样,类型不同,两个等号显示
3--值一样,类型同,三个等号显示
]]>在切换 v-if 块时,Vue.js 有一个局部编译/卸载过程,因为 v-if 之中的模板也可能包括数据绑定或子组件。v-if 是真实的条件渲染,因为它会确保条件块在切换当中合适地销毁与重建条件块内的事件监听器和子组件。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。
相比之下,v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。
v-if 是动态添加,当值为 false 时,是完全移除该元素,即 dom 树中不存在该元素。
v-show 仅是隐藏 / 显示,值为 false 时,该元素依旧存在于 dom 树中。若其原有样式设置了 display: none 则会导致其无法正常显示。
]]>一开始时按照h5的逻辑来,我以为是script在标签后面,所以才后渲染,导致判断失误的。以为时vue渲染的时候先吧标签渲染,然后才开始渲染script,但是这样的情况时不合理的,如果后加载标签的话,那么显示的时候标签上的颜色标签应该是不上色的情况。所以这个思路时错误的。
代码大致的结构是这样的,之前的代码删了,大致情况时这个样子。
<template>
<el-dialog v-model="visible" :title="$t('文件预览')" :close-on-click-modal="false" :close-on-press-escape="false">
<div v-if="fileType == 'jpg' ||
fileType == 'png' ||
fileType == 'ico' ||
fileType == 'gif' ||
fileType == 'webp'
">
<el-image style="width: 100%" :src="getFilePath"> </el-image>
</div>
<div v-else-if="fileType == 'docx' || fileType == 'doc'">文档
<embed :src="getFilePath" style="width: 100%; height: 600px" />
</div>
<div v-else-if="fileType == 'xlsx' || fileType == 'xls'">表格
<embed :src="getFilePath" style="width: 100%; height: 600px" />
</div>
<div v-else-if="fileType == 'pdf'">
<embed :src="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();
var getFilePath = "";
var fileType = "";
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);
}
};
//var showType: string;
// 获取信息
const getInfo = (uploadFileId: number) => {
baseService.get("/uploadFile/xyjqfileupload/" + uploadFileId).then((res) => {
Object.assign(dataForm, res.data);
if (dataForm.uploadFilePath != "") {
getFilePath = dataForm.uploadFilePath; //获取到图片的路径,写在这里面才管用,写在外面,第一次请求时候没有数据
//获取文件的类型,取文件后缀名判断
fileType = getFilePath.substring(getFilePath.lastIndexOf(".") + 1, getFilePath.length);
console.log("文件类型:" + fileType);
console.log(getFilePath);
} else {
ElMessage.error("链接地址为空,无法打开");
}
});
};
defineExpose({
init
});
</script>
代码大致时这个情况,后来发现主要原因出现在这两个赋值传递值的阶段
var filetype="";
var getFilePath="";
不知道是不是vue的结构还是html的结构问题,主要脚本语言时时typescript,需要const定义一下属性进行赋值,才可以在第一次传递数据和判断数据的时候生效,也就是说要改成下面的这个情况。而且在ts中,var的作用区间并不是和js一样,我想大概是因为这个情况吧,才学没多久,希望有人能指出这个bug来。。。。
const fileMessage = reactive({
getFilePath: "",
fileType: ""
});
这个是正确的结构:
<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="fileMessage.getFilePath" style="width: 100%; height: 600px" />
</div>
<div v-else-if="fileMessage.fileType == 'xlsx' || fileMessage.fileType == 'xls'">表格
<embed :src="fileMessage.getFilePath" style="width: 100%; height: 600px" />
</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);
}
};
//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);
} else {
ElMessage.error("链接地址为空,无法打开");
}
});
};
defineExpose({
init
});
</script>
]]>