You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中Excel文件导出无法自动下载问题排查

Excel导出问题:无法自动下载,但Network面板手动点击可触发

我点击导出按钮时,Excel文件无法自动下载,但在Chrome开发者工具的Network面板里点击export-status请求就能触发下载。已经设置Chrome允许自动下载,问题仍存在。请问这是浏览器新版本问题还是代码问题?


后端视图代码

@csrf_exempt
def export_status(request):
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT tev_incoming.id,tev_outgoing.dv_no AS dv_no, tev_incoming.code, tev_incoming.account_no, tev_incoming.id_no, tev_incoming.first_name, tev_incoming.middle_name,
                    tev_incoming.last_name, tev_incoming.date_travel, tev_incoming.division, tev_incoming.section, tev_incoming.status_id, au.first_name AS incoming_by,rb.first_name AS reviewed_by,
                    tev_incoming.original_amount, tev_incoming.final_amount, tev_incoming.incoming_in AS date_actual, tev_incoming.updated_at AS date_entry,
                    tev_incoming.incoming_out AS date_reviewed_forwarded, tev_bridge.purpose AS purposes
            FROM tev_incoming
            INNER JOIN (
                    SELECT MAX(id) AS max_id
                    FROM tev_incoming
                    GROUP BY code
            ) AS latest_ids
            ON tev_incoming.id = latest_ids.max_id
            LEFT JOIN tev_bridge
            ON tev_incoming.id = tev_bridge.tev_incoming_id
            LEFT JOIN tev_outgoing
            ON tev_bridge.tev_outgoing_id = tev_outgoing.id
            LEFT JOIN auth_user AS au
            ON au.id = tev_incoming.user_id
            LEFT JOIN auth_user AS rb
            ON rb.id = tev_incoming.reviewed_by
            ORDER BY tev_incoming.id DESC;
        """)
        rows = cursor.fetchall()

    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    response['Content-Disposition'] = 'attachment; filename={date}-TRIS-REPORT.xlsx'.format(
        date=datetime.now().strftime('%Y-%m-%d'),
    )

    workbook = Workbook()
    worksheet = workbook.active
    worksheet.title = 'REPORT'

    header_font = Font(name='Calibri', bold=True)
    centered_alignment = Alignment(horizontal='center')
    border_bottom = Border(
        bottom=Side(border_style='medium', color='FF000000'),
    )
    wrapped_alignment = Alignment(
        vertical='top',
        wrap_text=True
    )
    columns = [
        'DV NO',
        'CODE',
        'ACCOUNT NO',
        'ID NO',
        'FIRSTNAME',
        'MIDDLENAME',
        'LASTNAME',
        'DATE TRAVEL',
        'DIVISION',
        'SECTION',
        'STATUS ID',
        'INCOMING BY',
        'REVIEWED BY',
        'ORIGINAL AMOUNT',
        'FINAL_AMOUNT',
        'DATE ACTUAL RECEIVED',
        'DATE ENTRY',
        'DATE REVIEWED FORWARDED',
        'PURPOSE',
    ]
    row_num = 1
    for col_num, column_title in enumerate(columns, 1):
        cell = worksheet.cell(row=row_num, column=col_num)
        cell.value = column_title
        cell.font = header_font
        column_letter = get_column_letter(col_num)
        column_dimensions = worksheet.column_dimensions[column_letter]

    for tris in rows:
        row_num += 1
        row = [
            tris[1],  # dv_no
            tris[2],  # code
            tris[3],  # account_no
            tris[4],  # id_no
            tris[5],  # first_name
            tris[6],  # middle_name
            tris[7],  # last_name
            tris[8],  # date_travel
            tris[9],  # division
            tris[10],  # section
            tris[11],  # status_id
            tris[12],  # incoming_by
            tris[13],  # reviewed_by
            tris[14],  # original_amount
            tris[15],  # final_amount
            tris[16],
            tris[17],
            tris[18],  # date_reviewed_forwarded
            tris[19],  # purposes
        ]       
        for col_num, cell_value in enumerate(row, 1):
            cell = worksheet.cell(row=row_num, column=col_num)
            cell.value = cell_value
            column_letter = get_column_letter(col_num)
            column_dimensions = worksheet.column_dimensions[column_letter]
            column_dimensions.width = 19
    workbook.save(response)
    return response

前端AJAX代码

$.ajax({
   type: 'POST',
   url: "{% url 'export-status' %}"
}).done(function (data) {
         
});

问题原因及解决方法

这是前端代码的问题,和浏览器版本无关。

原因分析

AJAX请求本身不会自动处理Content-Disposition: attachment响应头来触发下载。浏览器只有在处理直接发起的请求(比如点击链接、表单提交)时,才会识别这类响应头并弹出下载框。而在Network面板手动点击请求时,浏览器是模拟直接请求的行为,所以能触发下载。

解决方法

替换AJAX请求为以下任意一种方式:

  1. 使用表单提交
    创建隐藏表单,点击导出按钮时提交:
<button id="exportBtn">导出Excel</button>
<form id="exportForm" method="POST" action="{% url 'export-status' %}" style="display:none;">
    {% csrf_token %}
</form>

<script>
document.getElementById('exportBtn').addEventListener('click', function() {
    document.getElementById('exportForm').submit();
});
</script>

(注:后端的@csrf_exempt可以移除,保留CSRF防护更安全)

  1. 直接跳转链接
    如果后端视图支持GET请求,可直接让按钮跳转:
<a href="{% url 'export-status' %}" class="btn">导出Excel</a>

或用JS触发跳转:

document.getElementById('exportBtn').addEventListener('click', function() {
    window.location.href = "{% url 'export-status' %}";
});
  1. 手动处理AJAX响应触发下载
    若必须使用AJAX,在回调里手动创建Blob触发下载:
$.ajax({
   type: 'POST',
   url: "{% url 'export-status' %}",
   xhrFields: {
       responseType: 'blob' // 指定响应为二进制类型
   }
}).done(function (data) {
    const url = window.URL.createObjectURL(data);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${new Date().toISOString().split('T')[0]}-TRIS-REPORT.xlsx`;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
});

内容的提问来源于stack exchange,提问作者Henry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:24:51