vue3笔记(13)文件流处理

年前赶项目真累啊~
系统需要实现文件下载、预览功能,文件流调试费了我好大功夫。这篇记录遇到的坑。

文件格式

后端读取文件后以流的形式传给我,从前端的角度理解就是Blob形式。
打开浏览器调试工具,resonpse需要携带这样的头:
response
注意在请求接口里面需要添加一个responseType

1
2
3
4
5
6
7
8
9
10
export function downloadFile(url: string, json: object) {
return axios({
url,
method: "post",
data: json,
responseType: "blob",
})
.then((res) => res)
.catch((error) => error);
}

下载

前端使用js-file-download读取Blob数据后可以直接实现浏览器下载。

1
2
3
4
5
6
import fileDownload from "js-file-download";
export default defineComponent({
setup() {
fileDownload(res.data, file.name);
}
})

预览

经过调试后,我觉得txt文件、图片、word文档比较适合直接预览,pdf适合打开新页面,使用Chrome浏览器的自带预览功能看起来更爽。
其中,word文档使用docx-preview包来实现预览。
文件格式对应type如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fileTypeMap = {
xls: "application/vnd.ms-excel",
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
doc: "application/msword",
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
pdf: "application/pdf",
ppt: "application/pdf",
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
png: "image/png",
gif: "image/gif",
jpeg: "image/jpeg",
jpg: "image/jpeg",
txt: "text/plain",
};

预览文件具体代码如下:

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
const handleqaPreviewFileRaw = (fileRaw, name) => {
let suffix = name.substring(name.indexOf(".") + 1);
const type = fileTypeMap[suffix];
if (suffix == "pdf") {
const url = URL.createObjectURL(new Blob([fileRaw], { type }));
window.open(url);
} else if (suffix == "txt") {
const url = URL.createObjectURL(new Blob([fileRaw], { type }));
state.txtUrl = url;
} else if (suffix == "docx" || suffix == "doc") {
state.docFile = true;
let docx = require("docx-preview");
nextTick(() => {
docx
.renderAsync(fileRaw, docContainer.value)
.then((x) => console.log("docx: finished", x));
});
} else if (suffix == "png" || suffix == "jpg" || suffix == "jpeg") {
const img = new Image();
const url = URL.createObjectURL(fileRaw);
state.imgUrl = url;
} else {
ElMessage.info("本文件暂不支持预览,请直接下载后查看,谢谢!");
}
};

拆成组件

父组件

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
<template>
<file-preview ref="fileRef" :fileObj="fileObj"></file-preview>
</template>
<script lang="ts">
export default defineComponent({
components: {
FilePreview,
},
setup() {
const fileRef = ref<any>();
const state = reactive({
fileObj: {
fileRaw: null,
name: "",
},
});

// 查看-触发子组件渲染
const handleqaPreviewFileRaw = (fileRaw, name) => {
fileRef.value.clearAll();
state.fileObj.fileRaw = fileRaw;
state.fileObj.name = name;
fileRef.value.handleqaPreviewFileRaw();
};

return {
fileRef,
...toRefs(state),
};
},
});
</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
34
35
36
37
38
39
40
41
42
43
44
<template>
<div class="previewList">
<iframe
v-if="txtUrl"
:src="txtUrl"
frameborder="0"
style="width: 100%; height: 100%; min-height: 500px"
></iframe>
<div v-if="imgUrl" class="imgPrew">
<img :src="imgUrl" alt="" />
</div>
<div v-if="docFile">
<div ref="docContainer"></div>
</div>
</div>
</template>

<script lang="ts">
export default defineComponent({
name: "FilePreview",
props: {
fileObj: {
type: Object,
}
},
setup(props, context) {
const state = reactive({
imgUrl: "",
txtUrl: "",
docFile: false,
});
const fileTypeMap = {};
const docContainer = ref(null);

const handleqaPreviewFileRaw = () => {};

return {
...toRefs(state),
docContainer,
handleqaPreviewFileRaw,
};
},
});
</script>

vue3笔记(13)文件流处理
https://guoningyan.com/2023/01/17/vue3笔记(13)文件流处理/
作者
Ningyan Guo
发布于
2023年1月17日
许可协议