前端预览文件总结(表格、图片、文本、视频)

文件在线预览

最近有个需求要在浏览器上能够查看文件。就是拿到文件的链接,实现文件在线预览的功能,在经过不断地查资料,试错后终于完成了下列文件格式的在线预览功能,暂时没有做大文件的处理(不知道咋做)。

  • 图片类(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-spreadsheetxlsx,特别要注意的是 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

先列举这么多,后续有其他类型的文件需要在线预览会接着更新

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值