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

Apache+Cpanel部署Django网站无法生成可下载PDF求助

解决Cpanel部署Django时PDF生成的io.UnsupportedOperation: fileno错误

问题背景

在Cpanel的Python Web App(基于Apache)部署的Django数据仪表盘上,点击下载按钮生成含用户提交数据的PDF时出现报错:控制台返回通用错误,webapp的stderr.log中显示io.UnsupportedOperation: fileno。本地开发环境完全正常,仅部署后出现问题。

相关代码如下:

JavaScript请求代码

function downloadPDF(id, date) {
    const csrftoken = getCookie('csrftoken');
    $.ajax({
      url: `/generate-pdf/${id}`,
      method: 'GET',
      headers: {
        'X-CSRFToken': csrftoken,
      },
      mode: 'same-origin',
      xhrFields: {
        responseType: 'blob' // 指定响应类型为blob
      },
      
      success: function(response) {
        console.log(response)
        var url = URL.createObjectURL(response);
        var link = document.createElement('a');
        link.href = url;
        link.download = `${id}-${date}.pdf`;
        link.click();
        URL.revokeObjectURL(url);
      },
      error: function(xhr, status, error) {
        console.error('Error generating PDF:', error);
      }
    });
  }

Django视图代码

def generate_pdf(request, id):
        buffer = io.BytesIO()
        doc = SimpleDocTemplate(buffer, pagesize=letter)
        # PDF生成逻辑(从数据库取数写入等)
        buffer.seek(0)
        return FileResponse(buffer, as_attachment=True, filename="gen_pdf.pdf")

原因分析

Apache搭配的WSGI环境对FileResponse的要求更严格:它期望传入的文件对象支持fileno()方法(用于获取文件描述符),但io.BytesIO是纯内存中的字节流对象,不具备这个属性,因此触发报错。而本地开发用的Django自带WSGI服务器对内存对象兼容性更好,所以不会出现问题。

解决方案

方案1:改用HttpResponse替代FileResponse

直接用HttpResponse返回字节流,手动设置响应头,绕开FileResponse对文件描述符的要求:

import io
from django.http import HttpResponse
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate

def generate_pdf(request, id):
    buffer = io.BytesIO()
    doc = SimpleDocTemplate(buffer, pagesize=letter)
    # 你的PDF生成逻辑(添加内容、从数据库取数等)
    buffer.seek(0)
    
    # 构造HttpResponse并设置正确的响应头
    response = HttpResponse(buffer, content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="gen_pdf.pdf"'
    return response

方案2:用临时文件替代内存字节流

如果PDF内容较大,用系统临时文件存储生成的内容,临时文件对象支持fileno()方法:

import io
import tempfile
from django.http import FileResponse
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate

def generate_pdf(request, id):
    # 创建临时文件,delete=False避免文件被立即删除
    with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
        doc = SimpleDocTemplate(temp_file, pagesize=letter)
        # 你的PDF生成逻辑
        temp_file.seek(0)
        response = FileResponse(temp_file, as_attachment=True, filename="gen_pdf.pdf")
        # 可选:后续可以通过os.unlink(temp_file.name)手动删除临时文件
        return response

方案3:简化前端下载逻辑(避免AJAX处理Blob)

对于GET请求,可以直接通过表单提交的方式触发下载,无需AJAX处理Blob,减少兼容性问题:

function downloadPDF(id, date) {
    const csrftoken = getCookie('csrftoken');
    // 通过表单提交发起GET请求,浏览器自动处理下载
    const form = document.createElement('form');
    form.method = 'GET';
    form.action = `/generate-pdf/${id}`;
    form.target = '_blank';
    
    // 添加CSRF令牌
    const csrfInput = document.createElement('input');
    csrfInput.type = 'hidden';
    csrfInput.name = 'csrfmiddlewaretoken';
    csrfInput.value = csrftoken;
    form.appendChild(csrfInput);
    
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);
}

验证方法

修改代码后重新部署到Cpanel,测试下载按钮功能,同时查看stderr.log确认是否还有fileno相关错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:05:20