文件在线预览
最近有个需求要在浏览器上能够查看文件。就是拿到文件的链接,实现文件在线预览的功能,在经过不断地查资料,试错后终于完成了下列文件格式的在线预览功能,暂时没有做大文件的处理(不知道咋做)。
- 图片类(jpg|jpeg|png|gif)
- 文本类 (txt|svg|log|sh|out|dat|mat|pdb|msg|pdbq|prt|sta|skf|shear|hhr|a3m|pbs|com|info)
- 视频类(mp4|avi|mkv|mov|webm|wmv)
- 表格类(xsl|xslx|xls|xlsx|csv)
- 。。。
图片类、视频类
这两种是最简单的,直接使用标签就可以。图片类使用 img,视频类使用 video
文本类
文本类有两种方法在线预览,
- 第一种直接使用
iframe
标签,一般文本类型的文件都可以在 iframe 中显示出来
<iframe
:src="previewUrl"
title="文件预览"
allowTransparency>
当前文件不支持预览,请直接下载
</iframe>
- 第二种是直接发请求,拿到文件内容,然后显示到页面上
//html
<template>
<div v-html="preHtml"></div>
</template>
// js
<script setup>
import { onMounted, ref } from 'vue';
import { string, nullable } from 'vue-types';
import axios from 'axios';
const props = defineProps({
previewUrl: oneOfType([string(), nullable()]),
});
const preHtml = ref('');
const getPreviewFile = () => {
axios({
method: 'get',
responseType: 'blob',
url: props.previewUrl,
}).then(({ data }) => {
data.text().then(res => {
preHtml.value = `<pre>${res}</pre>`;
}
});
});
};
onMounted(() => {
getPreviewFile();
});
</script>
表格类文件
表格类的预览采用了两个第三方的库,
x-data-spreadsheet
和xlsx
,特别要注意的是 csv 文件会有中文乱码问题,需要特殊处理,代码中有提到
xlsxOptions中的配置
<template>
<div id="xlsxPreviewId"></div>
</template>
<script setup>
import { watch, ref } from 'vue';
import { string, nullable } from 'vue-types';
import * as XLSX from 'xlsx'; // 预览excel文件
import axios from 'axios';
import Spreadsheet from 'x-data-spreadsheet';
import zhCN from 'x-data-spreadsheet/dist/locale/zh-cn';
import { stox, xlsxOptions, isUTF8 } from './utils';
Spreadsheet.locale('zh-cn', zhCN);
const props = defineProps({
previewUrl: oneOfType([string(), nullable()]),
});
const previewExcel = url => {
axios({
url,
method: 'GET',
responseType: 'arraybuffer',
})
.then(res => {
let grid = new Spreadsheet('#xlsxPreviewId', xlsxOptions);
// 判断文件格式是否是utf-8,不是给转化下
const buf = new Uint8Array(res.data);
let enc = new TextDecoder('utf-8');
let workbook;
if (isUTF8(buf)) {
// csv格式的文件会有中文乱码问题,在这里需要特殊处理
workbook = XLSX.read(enc.decode(buf), {
type: 'string',
codepage: 936,
});
} else {
workbook = XLSX.read(res.data);
}
grid.loadData(stox(workbook));
})
};
watch(
() => props.previewUrl,
newVal => {
if (newVal) {
previewExcel(newVal);
}
},
{
immediate: true,
}
);
</script>
utils 文件代码
import * as XLSX from 'xlsx'; // 预览excel文件
function stox(wb) {
var out = [];
wb.SheetNames.forEach(function(name) {
var o = { name: name, rows: {} };
var ws = wb.Sheets[name];
if (!ws || !ws['!ref']) return;
var range = XLSX.utils.decode_range(ws['!ref']);
range.s = { r: 0, c: 0 };
var aoa = XLSX.utils.sheet_to_json(ws, { raw: false, header: 1, range: range });
aoa.forEach(function(r, i) {
var cells = {};
r.forEach(function(c, j) {
cells[j] = { text: c };
var cellRef = XLSX.utils.encode_cell({ r: i, c: j });
if (ws[cellRef] != null && ws[cellRef].f != null) {
cells[j].text = '=' + ws[cellRef].f;
}
});
o.rows[i] = { cells: cells };
});
o.merges = [];
(ws['!merges'] || []).forEach(function(merge, i) {
if (o.rows[merge.s.r] == null) {
o.rows[merge.s.r] = { cells: {} };
}
if (o.rows[merge.s.r].cells[merge.s.c] == null) {
o.rows[merge.s.r].cells[merge.s.c] = {};
}
o.rows[merge.s.r].cells[merge.s.c].merge = [merge.e.r - merge.s.r, merge.e.c - merge.s.c];
o.merges[i] = XLSX.utils.encode_range(merge);
});
out.push(o);
});
return out;
}
function xtos(sdata) {
var out = XLSX.utils.book_new();
sdata.forEach(function(xws) {
var ws = {};
var rowobj = xws.rows;
var minCoord = { r: 0, c: 0 },
maxCoord = { r: 0, c: 0 };
for (var ri = 0; ri < rowobj.len; ++ri) {
var row = rowobj[ri];
if (!row) continue;
Object.keys(row.cells).forEach(function(k) {
var idx = +k;
if (isNaN(idx)) return;
var lastRef = XLSX.utils.encode_cell({ r: ri, c: idx });
if (ri > maxCoord.r) maxCoord.r = ri;
if (idx > maxCoord.c) maxCoord.c = idx;
var cellText = row.cells[k].text,
type = 's';
if (!cellText) {
cellText = '';
type = 'z';
} else if (!isNaN(Number(cellText))) {
cellText = Number(cellText);
type = 'n';
} else if (cellText.toLowerCase() === 'true' || cellText.toLowerCase() === 'false') {
cellText = Boolean(cellText);
type = 'b';
}
ws[lastRef] = { v: cellText, t: type };
if (type == 's' && cellText[0] == '=') {
ws[lastRef].f = cellText.slice(1);
}
if (row.cells[k].merge != null) {
if (ws['!merges'] == null) ws['!merges'] = [];
ws['!merges'].push({
s: { r: ri, c: idx },
e: { r: ri + row.cells[k].merge[0], c: idx + row.cells[k].merge[1] },
});
}
});
}
ws['!ref'] = minCoord ? XLSX.utils.encode_range({ s: minCoord, e: maxCoord }) : 'A1';
XLSX.utils.book_append_sheet(out, ws, xws.name);
});
return out;
}
const xlsxOptions = {
mode: 'read', // edit | read
showToolbar: false,
showGrid: true,
showContextmenu: false,
view: {
height: () => document.getElementById('xlsxPreviewId').clientHeight,
width: () => document.getElementById('xlsxPreviewId').clientWidth,
},
row: {
len: 99999,
height: 25,
},
col: {
len: 9999,
width: 100,
indexWidth: 60,
minWidth: 60,
},
style: {
bgcolor: '#ffffff',
align: 'left',
valign: 'middle',
textwrap: false,
strike: false,
underline: false,
color: '#0a0a0a',
font: {
name: 'Helvetica',
size: 10,
bold: false,
italic: false,
},
},
};
function isUTF8(bytes) {
var i = 0;
while (i < bytes.length) {
if (
// ASCII
bytes[i] == 0x09 ||
bytes[i] == 0x0a ||
bytes[i] == 0x0d ||
(0x20 <= bytes[i] && bytes[i] <= 0x7e)
) {
i += 1;
continue;
}
if (
// non-overlong 2-byte
0xc2 <= bytes[i] &&
bytes[i] <= 0xdf &&
0x80 <= bytes[i + 1] &&
bytes[i + 1] <= 0xbf
) {
i += 2;
continue;
}
if (
// excluding overlongs
(bytes[i] == 0xe0 &&
0xa0 <= bytes[i + 1] &&
bytes[i + 1] <= 0xbf &&
0x80 <= bytes[i + 2] &&
bytes[i + 2] <= 0xbf) || // straight 3-byte
(((0xe1 <= bytes[i] && bytes[i] <= 0xec) || bytes[i] == 0xee || bytes[i] == 0xef) &&
0x80 <= bytes[i + 1] &&
bytes[i + 1] <= 0xbf &&
0x80 <= bytes[i + 2] &&
bytes[i + 2] <= 0xbf) || // excluding surrogates
(bytes[i] == 0xed &&
0x80 <= bytes[i + 1] &&
bytes[i + 1] <= 0x9f &&
0x80 <= bytes[i + 2] &&
bytes[i + 2] <= 0xbf)
) {
i += 3;
continue;
}
if (
// planes 1-3
(bytes[i] == 0xf0 &&
0x90 <= bytes[i + 1] &&
bytes[i + 1] <= 0xbf &&
0x80 <= bytes[i + 2] &&
bytes[i + 2] <= 0xbf &&
0x80 <= bytes[i + 3] &&
bytes[i + 3] <= 0xbf) || // planes 4-15
(0xf1 <= bytes[i] &&
bytes[i] <= 0xf3 &&
0x80 <= bytes[i + 1] &&
bytes[i + 1] <= 0xbf &&
0x80 <= bytes[i + 2] &&
bytes[i + 2] <= 0xbf &&
0x80 <= bytes[i + 3] &&
bytes[i + 3] <= 0xbf) || // plane 16
(bytes[i] == 0xf4 &&
0x80 <= bytes[i + 1] &&
bytes[i + 1] <= 0x8f &&
0x80 <= bytes[i + 2] &&
bytes[i + 2] <= 0xbf &&
0x80 <= bytes[i + 3] &&
bytes[i + 3] <= 0xbf)
) {
i += 4;
continue;
}
return false;
}
return true;
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
export { stox, xtos, xlsxOptions, isUTF8, ab2str };
END
先列举这么多,后续有其他类型的文件需要在线预览会接着更新