前端实现文件下载

1,127 阅读2分钟

1. a标签直接下载

H5利用<a>标签的href属性直接进行下载, 后端接口只需要返回一个下载地址(URL)即可,不能跨域下载

// 如果不加download 则会将txt , pdf, 图片 等直接打开预览,而不是下载 
// href 如果不是完整的url,只是一个路径,如/xxx/xxx/test.csv 则会在前面自动拼接域名

<a href="下载地址" download>

// 如果后面加了文件名,则下载的时候,会将文件名改为download的属性值,
// 注意:若后端设置了响应头 Content-Disposition字段中设置了 filename,
// 则download="文件名"不会起效果

<a href="下载地址" download='文件名'>

如果后端返回了一个文件地址,下载txt的时候,需要配置跨域

export function getFileAndDownload(fileName: string, url: string) {
  var x = new XMLHttpRequest();
  x.open('GET', url, true);
  x.responseType = 'blob';
  x.onload = function (e) {
    var blob = x.response;
    var a = document.createElement('a');
    a.style.display = 'none';
    a.download = fileName;
    a.href = URL.createObjectURL(blob);
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };
  x.send();
}

2. blob数据下载

利用服务端返回的blob数据,来进行转换下载。将服务端的blob数据存储到本地,主要还是用到a链接

完整代码: 兼容问题暂未考虑

const downloadFile = (blobData: Blob, fileName: string) => {
    const blob = new Blob([blobData]); // 创建blob对象
    const ElementA = document.createElement('a');
    // ElementA.style.display = 'none'
    ElementA.href = URL.createObjectURL(blob);
    ElementA.download = fileName;
    document.body.appendChild(ElementA);
    ElementA.click();
    document.body.removeChild(ElementA); // 移除a
    URL.revokeObjectURL(ElementA.href) // 释放url内存
}

3. 下载大文件

注意: 如果下载特别大的文件,则上面的就不行了,可能会造成网页崩溃,这里就需要用到下载的库。 FileSaver github.com/eligrey/Fil…, 轻松实现下载文件500M

4. blob与base64数据相互转换

有的图片可以使用base64来进行展示,直接将base64地址放到img的src 中就可以展示出来

base64转换blob数据

function dataURLToBlob(dataURL: string) {
  const parts = dataURL.split(/[:;,]/);
  const mime = parts[1];
  const decoder = parts[2] === 'base64' ? atob : decodeURIComponent;
  const binary = decoder(parts.pop());
  const array = binary.split('').map((item, i) => binary.charCodeAt(i));
  return new BlobConstructor([new Uint8Array(array)], { type: mime });
}

blob数据转换base64

function blobToDaTaURL(blobData: string) {
  const blob = blobData;
  const reader = new FileReader()
  reader.readAsDataURL(blob) // 开始读取指定blob中的内容
  reader.onload = (e) => { 
  // 读取完成后的result属性中将包含一个data: URL格式的Base64字符串
     以表示所读取文件的内容。
  	const base64String = e.target.result;
    // 如果是图片,则可以直接写到src展示出来
  }
}

下载为csv文件

1.可以直接使用下载的一个库 react-csv


// 导入 react-csv
import { CSVLink } from 'react-csv';

const csvRef = useRef(null);

const handleClick = () => {
    csvRef.current.link.click();
};

<Button onClick={handleClick} />
<CSVLink filename={`${filename}.csv`} data={dataSource} ref={csvRef} />
  1. 自己处理数据进行下载

    // 将要下载的数据转换为这种格式 ,\n 代表换行, 逗号代表空
     const csvArray = '标题一, 标题二, \n x1,x2,x3,x4,x5,x6,x7 \n a1,a2,a3,a4,,,a6';

    // 转为blob下载
    const blob = new Blob([`\uFEFF${csvArray}`], {
      type: 'text/csv; charset=utf-8',
    });
    
    downloadCsv(blob, '测试');
    
   // 下载
   const downloadCsv = (blob: Blob, name: string = 'data') => {
    const fileName = `${name}`;
    const data = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.setAttribute('href', data);
    link.setAttribute('download', fileName);
    link.click();
  };

  1. 上传解析csv 文件
 const uploadData = (file: File) => {
    if (!/\.csv$/.test(file.name)) {
      message.error('请选择csv文件上传');
      return;
    }
    const reader = new FileReader();
    reader.onload = async () => {
      const content = reader.result as string;
      const rows = content.split('\n');
     
      if (rows.length < 2) {
        return;
      }
     
      const res: UploadFileType[] = [];
      rows.splice(0, 1);
      // 手动处理上传的数据
      let count = 0;
      rows.forEach((row) => {
        if (row) {
          if (row.startsWith(',,,,')) {
            let str = row.slice(4);
            if (str.includes('(') || str.includes('[')) {
              str = str.replace(',', '*');
            }
            const arr = str.split(',');
            let label = arr[0];
            if (label.startsWith('"')) {
              label = label.slice(1, label.length - 1);
            }
            label = label.replace('*', ', ');
            const tmp = res[res.length - 1];
            tmp.bins.push({
              iv: parseFloat(arr[5]),
              positive_count: arr[1],
              total_count: arr[2],
              negative_count: parseInt(arr[2], 10) - parseInt(arr[1], 10),
              woe: parseFloat(arr[6]),
              index: (count += 1),
            });
          } else {
            const cols = row.split(',');
            res.push({
              iv: parseFloat(cols[2]),
              type: cols[1],
              bin_count: parseInt(cols[3], 10),
              bins: [],
            });
            count = 0;
          }
        }
      });
    };

    reader.readAsText(file);
    // eslint-disable-next-line consistent-return
    return false;
  };

 <Upload action="" beforeUpload={uploadData} showUploadList={false}>
     <Button>上传文件</Button>
 </Upload>