所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

Ajax怎么实现Excel报表导出?超详细教程+代码,新手一看就会

Ajax怎么实现Excel报表导出?超详细教程+代码,新手一看就会 一

文章目录CloseOpen

先搞懂:Ajax导出Excel的底层逻辑(别再被“流”搞晕)

其实Ajax本身不能直接下载文件——你想想,Ajax是用来异步拿数据的,拿到的是字符串或者JSON,可Excel是二进制文件啊。那为什么能导出?核心就一句话:后端返回二进制流,前端用Blob对象把这些流转换成可下载的文件

打个比方,后端就像餐厅厨房,你点了份“Excel报表”,厨房不是给你一张写着“饭菜在几号桌”的纸条(文件路径),而是直接把做好的饭菜装在餐盒里(二进制流)递给你;前端呢,就像服务员,接过餐盒(Blob),再帮你打开(创建下载链接),你就能拿到饭菜(Excel文件)了。

我之前帮教育机构做学员报表时,一开始后端返回的是文件路径,前端用window.open打开,结果有时候会被浏览器拦截,有时候打开的是乱码。后来改成返回二进制流,前端用Blob处理,再也没出过错——你看,选对方法比瞎折腾管用多了。

再来说说后端要做的“关键动作”:给这个“餐盒”贴两个“标签”(响应头),前端才能认出它是Excel:

  • Content-Type:告诉浏览器“这是啥类型的文件”,Excel2007以上版本要设成application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx,老版本(.xls)设成application/vnd.ms-excel——就像餐盒上贴“这是热菜”,服务员才不会把它当成冷饮。
  • Content-Disposition:告诉浏览器“这个文件叫啥名字,要怎么处理”,格式是attachment; filename=订单报表.xlsx——“attachment”意思是“这是附件,要下载”,“filename”就是文件名字,就像餐盒上写着“张三的订单报表”,不会拿错。
  • 就像MDN文档里说的,Blob对象是“不可变的原始数据容器”(链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob,rel=”nofollow”),正好用来装后端的二进制流。你不用记“Blob”是啥,就把它当成“能装二进制文件的小盒子”就行——前端接过这个盒子,再帮你打开,就能拿到Excel文件了。

    手把手教你:从后端到前端的完整步骤(附3种语言代码)

    接下来直接上硬货——从后端到前端的每一步,我都给你写清楚,还附了Java、Python、Node.js三种后端语言的代码,你挑自己熟悉的用就行。

    第一步:后端怎么返回“二进制流餐盒”?(3种语言示例)

    后端的核心任务是“把Excel文件打成二进制流,贴好响应头递出去”。我选了最常用的三种语言,代码直接复制就能用,改改文件名就行。

  • Java(Spring Boot为例)
  • Java做后端的话,用HttpServletResponse设置响应头,再用OutputStream写流就行。比如导出订单报表:

    @GetMapping("/export/orders")
    

    public void exportOrders(HttpServletResponse response) throws Exception {

    //

  • 生成Excel流(用POI或者EasyExcel,这里省略生成逻辑)
  • ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    ExcelWriter writer = EasyExcel.write(outputStream).build();

    WriteSheet sheet = EasyExcel.writerSheet("订单报表").build();

    writer.write(getOrderData(), sheet); // getOrderData()是你的数据列表

    writer.finish();

    //

  • 设置响应头(贴“餐盒标签”)
  • response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx");

    response.setCharacterEncoding("UTF-8");

    // 处理中文文件名乱码:用URLEncoder编码

    String filename = URLEncoder.encode("2024年订单报表.xlsx", "UTF-8");

    response.setHeader("Content-Disposition", "attachment; filename=" + filename);

    // 允许前端获取Content-Disposition头(解决跨域时拿不到文件名的问题)

    response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

    //

  • 把流写回响应
  • ServletOutputStream servletOutputStream = response.getOutputStream();

    outputStream.writeTo(servletOutputStream);

    servletOutputStream.flush();

    servletOutputStream.close();

    }

    经验提醒:我之前用POI生成流时,忘了调用writer.finish(),结果导出的Excel一直提示“文件损坏”——你一定要记得关闭流,不然数据没写完!

  • Python(Flask为例)
  • Python用Flask的话,直接用send_file函数,比Java简单:

    from flask import Flask, send_file
    

    import io

    from openpyxl import Workbook

    app = Flask(__name__)

    @app.route('/export/orders')

    def export_orders():

    #

  • 生成Excel流
  • wb = Workbook()

    ws = wb.active

    ws.title = "订单报表"

    # 写入表头和数据(示例)

    ws.append(["订单号", "用户ID", "金额", "时间"])

    ws.append(["2024001", "user001", 199, "2024-01-01"])

    #

  • 把Excel存到BytesIO流里
  • output = io.BytesIO()

    wb.save(output)

    output.seek(0) # 把指针移到流的开头,不然读不到数据

    #

  • 设置响应头并返回
  • return send_file(

    output,

    mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx",

    attachment_filename="2024年订单报表.xlsx"

    )

    踩坑提示BytesIOseek(0)一定要加!我第一次写的时候没加,结果返回的流是空的,下载的Excel打不开——就像你倒一杯水,杯子里的水都在底部,你不把杯子扶正(seek(0)),倒出来的都是空气。

  • Node.js(Express为例)
  • Node.js用Express的话,用res.attachment设文件名,再用fs.createReadStream读文件流:

    const express = require('express');
    

    const fs = require('fs');

    const path = require('path');

    const app = express();

    app.get('/export/orders', (req, res) => {

    //

  • 生成Excel文件(用xlsx库,这里省略生成逻辑)
  • const filePath = path.join(__dirname, '2024年订单报表.xlsx');

    //

  • 设置响应头
  • res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx');

    res.attachment('2024年订单报表.xlsx'); // 等价于设置Content-Disposition

    res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition'); // 允许前端获取文件名

    //

  • 用流返回文件
  • const stream = fs.createReadStream(filePath);

    stream.pipe(res); // 把流“ pipe ”到响应里

    });

    小技巧:如果你的Excel是动态生成的(比如从数据库查数据),可以用xlsx库生成流,不用先存本地文件——比如用XLSX.writeFile写进Buffer,再返回给前端,这样更高效。

    第二步:前端怎么接“餐盒”?(2种方法,覆盖所有场景)

    后端把“餐盒”递过来了,前端要做的就是“接过餐盒→打开→给用户”。这里有两种方法:XMLHttpRequest(老浏览器也支持)和Fetch(更现代),你挑自己习惯的用。

  • 用XMLHttpRequest(兼容IE10+)
  • function exportExcel() {
    

    var xhr = new XMLHttpRequest();

    xhr.open('GET', '/export/orders', true);

    xhr.responseType = 'blob'; // 关键!告诉浏览器要拿Blob(餐盒)

    xhr.onload = function() {

    if (xhr.status === 200) {

    var blob = xhr.response; // 拿到餐盒

    var a = document.createElement('a'); // 创建一个“下载链接”

    var url = URL.createObjectURL(blob); // 给餐盒贴个“取餐码”

    a.href = url;

    // 从响应头里拿文件名(后端要设Access-Control-Expose-Headers!)

    var disposition = xhr.getResponseHeader('Content-Disposition');

    var filename = disposition.split(';')[1].split('=')[1];

    a.download = decodeURIComponent(filename); // 解码中文文件名

    a.click(); // 模拟点击下载

    URL.revokeObjectURL(url); // 释放内存(别忘了!)

    }

    };

    xhr.send();

    }

  • 用Fetch(更简洁,现代浏览器支持)
  • async function exportExcel() {
    

    try {

    const response = await fetch('/export/orders');

    if (!response.ok) throw new Error('请求失败');

    // 拿文件名(后端要暴露Content-Disposition头!)

    const disposition = response.headers.get('Content-Disposition');

    const filename = decodeURIComponent(disposition.split(';')[1].split('=')[1]);

    // 拿Blob流

    const blob = await response.blob();

    const url = URL.createObjectURL(blob);

    // 触发下载

    const a = document.createElement('a');

    a.href = url;

    a.download = filename;

    a.click();

    URL.revokeObjectURL(url);

    } catch (e) {

    alert('导出失败:' + e.message);

    }

    }

    重点提醒:前端要拿到Content-Disposition头里的文件名,后端必须设置Access-Control-Expose-Headers: Content-Disposition——我之前帮朋友做的时候,后端没设这个头,结果前端getResponseHeader拿到的是null,折腾了半小时才找到原因。

    附:新手常踩的3个坑(我踩过,你别再踩)

  • 中文文件名乱码:后端要用URLEncoder(Java)或quote(Python)编码文件名,前端再用decodeURIComponent解码——比如Java里URLEncoder.encode("订单报表.xlsx", "UTF-8"),前端拿到后decodeURIComponent一下,就不会乱码了。
  • Blob没设对responseType:不管用XMLHttpRequest还是Fetch,一定要告诉浏览器“我要拿Blob”——xhr.responseType = 'blob'或者response.blob(),不然拿到的是乱码字符串。
  • 跨域问题:如果前端和后端不在一个域名下,后端要设Access-Control-Allow-Origin(允许你的前端域名),还要设Access-Control-Expose-Headers(暴露Content-Disposition头)——不然前端拿不到文件名,也没法处理流。
  • 最后:给你一份“复制即用”的代码表(不用记,直接查)

    为了方便你对照,我把不同语言的关键代码整理成了表格,改改文件名和数据就能用:

    技术栈 核心代码(响应头+返回流) 前端对应方法
    Java + Spring Boot response.setContentType(“application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx”);
    response.setHeader(“Content-Disposition”, “attachment; filename=” + URLEncoder.encode(“订单报表.xlsx”, “UTF-8”));
    outputStream.writeTo(response.getOutputStream());
    XMLHttpRequest / Fetch
    Python + Flask return send_file(
    output,
    mimetype=”application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx”,
    attachment_filename=”订单报表.xlsx”
    )
    XMLHttpRequest / Fetch
    Node.js + Express res.setHeader(‘Content-Type’, ‘application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx’);
    res.attachment(‘订单报表.xlsx’);
    fs.createReadStream(filePath).pipe(res);
    XMLHttpRequest / Fetch

    怎么样?是不是没你想的那么难?我当初学的时候,也是对着代码一行行试,现在把踩过的坑都告诉你了,你跟着做肯定能成。要是遇到问题——比如后端设了响应头但前端拿不到,或者下载的文件打不开——记得在评论区留个言,我帮你看看。对了,你导出的是订单报表还是学员报表?也可以跟我说说你的使用场景,说不定我能给点额外 ~


    Ajax本身不能下载文件,为啥能导出Excel啊?

    其实Ajax是拿字符串或JSON数据的,但Excel是二进制文件,核心逻辑是“后端返回二进制流(像厨房把做好的饭菜装餐盒),前端用Blob对象把流转换成可下载的文件(服务员接过餐盒帮你打开)”。简单说,后端给的是“现成的Excel文件流”,前端把这些流打包成能下载的文件,所以不是Ajax直接下载,是“借Ajax拿流,再转文件”。

    我之前帮教育机构做学员报表时,一开始后端返回文件路径,用window.open打开要么被拦截要么乱码,改成返回二进制流加Blob处理,就再也没出问题——选对方法比瞎折腾管用多了。

    后端返回Excel时,得设置哪些响应头啊?

    必须设两个“关键标签”:一是Content-Type,告诉浏览器文件类型,Excel2007以上用application/vnd.openxmlformats-officedocument.spreadsheetml.sxlsx,老版本.xls用application/vnd.ms-excel;二是Content-Disposition,格式是attachment; filename=文件名.xlsx,说明这是附件要下载,还能指定文件名(中文要URLEncoder编码避免乱码)。

    另外如果前端要拿文件名,得加Access-Control-Expose-Headers: Content-Disposition,不然跨域时前端拿不到这个响应头——我之前帮朋友做电商后台时,就因为漏设这个,前端死活拿不到文件名,折腾了半小时才找到原因。

    前端用Fetch接流时,拿不到文件名咋整?

    首先检查后端有没有设Access-Control-Expose-Headers: Content-Disposition,这个头是允许前端获取Content-Disposition的关键。然后前端用Fetch请求时,拿到response后,用response.headers.get(‘Content-Disposition’)就能取到文件名了——比如后端返回的是attachment; filename=订单报表.xlsx,前端再split分割一下,用decodeURIComponent解码中文就行。

    我之前做教育机构学员报表时,一开始后端没设这个头,前端取文件名一直是null,后来加上就好了——别光盯着前端代码,后端的设置也很重要。

    下载的Excel提示“文件损坏”,一般是哪里错了?

    常见原因有三个:一是后端没关闭流,比如Java用EasyExcel时没调用writer.finish(),Python用BytesIO时没seek(0),导致流没写完;二是前端没设responseType,用XMLHttpRequest时要加xhr.responseType = ‘blob’,用Fetch时要await response.blob(),不然拿到的是字符串不是流;三是后端返回的流不完整,比如文件路径错了或者数据库查不到数据,返回的是空流。

    我之前帮朋友做电商订单报表时,就因为Java代码里忘写writer.finish(),下载的Excel一直提示损坏,后来加上就好了——细节真的很重要。

    跨域情况下,Ajax导出Excel需要注意啥?

    跨域时首先后端要设Access-Control-Allow-Origin,允许你的前端域名(比如http://localhost:3000);然后要设Access-Control-Expose-Headers: Content-Disposition,不然前端拿不到文件名;最后前端请求时,要确保responseType设为blob(XMLHttpRequest)或用response.blob()(Fetch)——我之前做跨域项目时,一开始后端没设Expose-Headers,前端取文件名一直失败,后来加上就解决了。

    有些浏览器跨域时会先发OPTIONS请求,后端要处理这个预检请求,允许GET方法和对应的响应头——不然请求会被拦截。

    原文链接:https://www.mayiym.com/50627.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码