【问题标题】:How to return PDF file in an Graphql mutation?如何在 Graphql 突变中返回 PDF 文件?
【发布时间】:2020-07-27 18:46:26
【问题描述】:

我在前端使用 React 和 Graphql,在后端使用 Django 和 Graphene。

我希望能够下载报告的 pdf 文件。我尝试使用 mutation 来做到这一点,如下所示:

const [createPdf, {loading: createPdfLoading, error: createPdfError}] = useMutation(CREATE_PDF)
const handleCreatePDF = async (reportId) => {
        const res = await createPdf({variables: {reportId: parseInt(reportId) }})
        debugger;
    };

export const CREATE_PDF = gql`
    mutation ($reportId: Int!) {
        createPdf (reportId: $reportId){
            reportId
        }
    }
`;

在后端我有这样的东西:

class CreatePDFFromReport(graphene.Mutation):
    report_id = graphene.Int()

    class Arguments:
        report_id = graphene.Int(required=True)

    def mutate(self, info, report_id):
        user = info.context.user

        if user.is_anonymous:
            raise GraphQLError("You are not logged in!")

        report = Report.objects.get(id=report_id)
        if not report:
            raise GraphQLError("Report not found!")

        if user != report.posted_by:
            raise GraphQLError("You are not permitted to do that!")

        html_string = render_to_string('report.html', {'report_id': 1})

        pdf_file = HTML(string=html_string)
        response = HttpResponse(pdf_file, content_type='application/pdf')
        response['Content-Disposition'] = 'attachment; filename="rapport_{}"'.format(report_id)
        return response


        # return CreatePDFFromReport(report_id=report_id)

当我取消注释 return CreatePDFFromReport(report_id=report_id) 时,它工作正常。

但我想返回 pdf 文件。

有没有可能这样做?

谢谢。

【问题讨论】:

  • @xadm 最好的方法是什么?将其添加为答案,我会接受。

标签: python django reactjs graphql apollo


【解决方案1】:

仅靠变异是不行的。

您不能使用 json'ed 通信返回文件(二进制)/标题(mime)/等(需要被浏览器作为下载请求行为处理)。 GraphQL 请求和响应都是 json 格式。

解决方案

工作流程:

  • 调用突变(在变量中传递参数)
  • 解析器创建文件内容并将其保存在某处(服务器或 s3 上的文件存储)
  • 解析器返回一个文件id作为下载网址的一部分或完整的url(字符串)到目标文件

idurl 在这种情况下应该是所需突变响应的一部分

 mutation ($reportId: Int!) {
     createPdf (reportId: $reportId){
        reportDownloadId
    }
 }
  • 突变结果(以及我们的reportDownloadId)在data 中可用

可以通过两种方式访问​​来自突变的data(结果/响应):

...{ data }

const [addTodo, { data }] = useMutation(ADD_TODO);

...或onCompleted处理程序:

addTodo({ 
  variables: { type: input.value },
  onCompleted = {(data) => {
    // some action with 'data'
    // f.e. setDownloadUrl(data.reportDownloadId)
    // from 'const [downloadUrl, setDownloadUrl] = useState(null)'
  }}
});

docs中描述了这两种方法

这两种方法都允许您(有条件地)呈现下载按钮/链接/组件,f.e.

{downloadUrl && <DownloadReport url={downloadUrl}/>}

{downloadUrl && <a href={downloadUrl}>Report ready - download it</a>}

// render download, share, save in cloud, etc.

Handler方法可用于自动调用下载文件请求,无需渲染额外的按钮。这个看起来像一个动作,而从技术上讲有两个请求(graphql 突变和下载)。这个thread 描述了如何使用 js 处理下载。

【讨论】:

    【解决方案2】:

    您可以将 PDF 编码为 Base64 并将其作为字符串发送。 然后在前端解码。

    注意Base64每3个字节使用4个字节编码(3MB文件变成4MB字符串)。

    【讨论】:

    • 当然,但要多 33% ... 适合嵌入图标、svg、小文件 ... 或小规模的方法
    猜你喜欢
    • 2018-12-19
    • 2018-06-06
    • 2021-03-01
    • 2021-06-25
    • 2019-06-28
    • 2020-12-22
    • 2021-04-01
    • 2018-10-17
    • 2021-02-27
    相关资源
    最近更新 更多