沃梦达 / IT编程 / 前端开发 / 正文

vue使用axios实现excel文件下载的功能

下面是使用Vue和Axios实现Excel文件下载的攻略,过程中将会包含两条示例说明。

下面是使用Vue和Axios实现Excel文件下载的攻略,过程中将会包含两条示例说明。

准备工作

  1. 安装依赖:npm install --save axios file-saver xlsx

其中,axios 是我们将用来与后端交互的网络请求库;file-saver 是将文件保存到本地的库;xlsx 将Excel文件转换为二进制格式。

  1. main.js 中导入所需的库:
import Vue from 'vue'
import axios from 'axios'
import XLSX from 'xlsx'
import FileSaver from 'file-saver'

Vue.prototype.$http = axios
Vue.prototype.$XLSX = XLSX
Vue.prototype.$fileSaver = FileSaver

前端部分

  1. 编写前端页面 (HTML/CSS/JS),通过一个按钮来触发下载请求:
<template>
  <div>
    <button @click="download">下载Excel文件</button>
  </div>
</template>

<script>
export default {
  methods: {
    download() {
      this.$http
        .get('/api/download')
        .then(resp => {
          const blob = new Blob([resp.data], {type: 'application/vnd.ms-excel'})
          this.$fileSaver.saveAs(blob, 'example.xlsx')
        })
        .catch(error => {
          console.log(error)
        })
    }
  }
}
</script>

这个页面只有一个按钮,点击按钮时触发 download 方法。download 方法使用 $http 发送请求到后端,获取Excel文件的二进制数据。通过 Blob$fileSaver 将这个二进制数据转换为文件,并将文件保存到本地。

  1. 完成前端代码

在上面的代码中,文件名是写死的,我们可以通过后端接口返回的头信息中获取文件名并在前端进行设置。

<template>
  <div>
    <button @click="download">下载Excel文件</button>
  </div>
</template>

<script>
export default {
  methods: {
    download() {
      this.$http
        .get('/api/download', {responseType: 'blob'})
        .then(resp => {
          const disposition = resp.headers['content-disposition']
          const fileName = /filename="(.*?)"/.exec(disposition)[1]
          const blob = new Blob([resp.data], {type: 'application/vnd.ms-excel'})
          this.$fileSaver.saveAs(blob, fileName)
        })
        .catch(error => {
          console.log(error)
        })
    }
  }
}
</script>

后端部分

  1. 编写后端接口 (Node.js),生成Excel文件并返回给前端:
const XLSX = require('xlsx')

function generateExcel() {
  const worksheet = XLSX.utils.json_to_sheet([
    {name: 'apple', price: 1.99},
    {name: 'banana', price: 0.99},
    {name: 'orange', price: 1.49},
    {name: 'watermelon', price: 3.49}
  ])
  const workbook = XLSX.utils.book_new()
  XLSX.utils.book_append_sheet(workbook, worksheet, 'example')
  return XLSX.write(workbook, {type: 'buffer', bookType: 'xlsx'})
}

app.get('/api/download', (req, res) => {
  const buffer = generateExcel()
  res.set({
    'Content-Type': 'application/vnd.ms-excel',
    'Content-Disposition': 'attachment;filename=example.xlsx'
  })
  res.send(buffer)
})

这个接口使用 XLSX 生成一个包含一些商品信息的Excel文件并将其返回。在响应头中设置了文件类型和文件名。

  1. 完成后端代码

在上面的代码中,文件名是写死的,我们可以通过查询参数中传递的文件名和文件类型来进行设置。

const XLSX = require('xlsx')

function generateExcel() {
  const worksheet = XLSX.utils.json_to_sheet([
    {name: 'apple', price: 1.99},
    {name: 'banana', price: 0.99},
    {name: 'orange', price: 1.49},
    {name: 'watermelon', price: 3.49}
  ])
  const workbook = XLSX.utils.book_new()
  XLSX.utils.book_append_sheet(workbook, worksheet, 'example')
  return XLSX.write(workbook, {type: 'buffer', bookType: 'xlsx'})
}

app.get('/api/download', (req, res) => {
  const buffer = generateExcel()
  const fileName = req.query.fileName || 'example.xlsx'
  const fileType = req.query.fileType || 'xlsx'
  res.set({
    'Content-Type': `application/vnd.ms-${fileType}`,
    'Content-Disposition': `attachment;filename="${fileName}"`
  })
  res.send(buffer)
})

示例说明

下面分别为实际情况下的前后端实现示例。

示例一:后端使用Java Spring框架

@GetMapping("/download")
public void download(HttpServletResponse response) throws IOException {
    byte[] bytes = generateExcel();
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-Disposition", "attachment;filename=example.xlsx");
    try (OutputStream outputStream = response.getOutputStream()) {
        outputStream.write(bytes);
        outputStream.flush();
    }
}

private byte[] generateExcel() throws IOException {
    List<Map<String, Object>> list = new ArrayList<>();
    list.add(Maps.of("name", "apple", "price", 1.99));
    list.add(Maps.of("name", "banana", "price", 0.99));

    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("example");
    int rownum = 0;

    XSSFRow header = sheet.createRow(rownum++);
    header.createCell(0).setCellValue("Name");
    header.createCell(1).setCellValue("Price");

    for (Map<String, Object> map : list) {
        XSSFRow row = sheet.createRow(rownum++);
        row.createCell(0).setCellValue(map.get("name").toString());
        row.createCell(1).setCellValue(Double.parseDouble(map.get("price").toString()));
    }

    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        workbook.write(baos);
        return baos.toByteArray();
    }
}

与前文中的实现稍有不同,但实现的内容基本相同。

示例二:前端使用react+typescript,后端使用nest.js

import axios from "axios";
import React, { useCallback } from "react";
import { saveAs } from "file-saver";
import XLSX from "xlsx";

export default function ExampleComponent() {
  const download = useCallback(() => {
    axios
      .get("/api/download", { responseType: "blob" })
      .then((resp) => {
        const disposition = resp.headers["content-disposition"];
        const fileName = /filename="(.*?)"/.exec(disposition)[1];
        const blob = new Blob([resp.data], { type: "application/vnd.ms-excel" });
        saveAs(blob, fileName);
      })
      .catch((error) => {
        console.error(error);
      });
  }, []);

  return (
    <div>
      <button onClick={download}>下载Excel文件</button>
    </div>
  );
}

前端部分与前文的实现基本相同,只是更换使用的网络请求库和文件保存库,以及按typescript的语法规定书写。

后端部分也较为简单,只需要在 Nest.js 的控制器中提供下载接口即可。这里使用一个自定义装饰器(SetHeaders)来设置响应头信息,以增加灵活性。

import { Controller, Get, SetHeaders } from "@nestjs/common";
import { Response } from "express";
import { generateExcel } from "./generate-excel";
import { ApiTags } from "@nestjs/swagger";

@Controller()
@ApiTags("Example")
export class ExampleController {
  @Get("/api/download")
  @SetHeaders({
    "Content-Type": "application/vnd.ms-excel",
    "Content-Disposition": 'attachment;filename="example.xlsx"'
  })
  async download(@Response() res: Response) {
    const buffer = await generateExcel();
    res.write(buffer);
    res.end();
  }
}

可以看到,在 download 方法上使用 @SetHeaders 装饰器设置了响应头信息,并在控制器上使用 @ApiTags 装饰器添加了接口所属的标签,以便 Swagger 在生成文档时更好地分类和展示接口。而 generateExcel 方法则参考了前文中的实现,生成了一个带有一些商品信息的 Excel 表格。

本文标题为:vue使用axios实现excel文件下载的功能