如何在 Graphql 突变中 return PDF 文件?
How to return PDF file in an Graphql mutation?
我在前端使用 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)
时它工作正常。
但是我想要returnpdf文件。
有没有可能做到这一点?
谢谢。
单靠变异是做不到的。
您不能return file(binary)/headers(mime)/etc(需要由浏览器作为下载请求行为处理)使用json'ed 通信。 GraphQL 请求和响应采用 json 格式。
解决方案
工作流程:
- 调用突变(在变量中传递参数)
- 解析器创建文件内容并将其保存在某处(文件存储在服务器或 s3 上)
- 解析器 return 文件
id
是下载的一部分 url 或完整 url
(字符串)到目标文件
id
或 url
在这种情况下应该是所需突变反应的一部分
mutation ($reportId: Int!) {
createPdf (reportId: $reportId){
reportDownloadId
}
}
- 突变结果(以及我们的
reportDownloadId
)在 data
中可用
data
(result/response) from mutation 可以通过两种方式访问:
... 通过 { 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
中都有描述
这两种方法都允许您(有条件地)呈现下载 button/link/component、f.e。
{downloadUrl && <DownloadReport url={downloadUrl}/>}
{downloadUrl && <a href={downloadUrl}>Report ready - download it</a>}
// render download, share, save in cloud, etc.
Handler 方法可用于自动调用下载文件请求,无需渲染额外按钮。这个 看起来像一个动作,而技术上有两个后续请求 (graphql 突变和下载)。这个描述了如何使用js处理下载。
您可以将 PDF 编码为 Base64 并将其作为字符串发送。
然后在前端解码即可。
请注意,Base64 使用 4 个字节来编码每 3 个字节(3MB 文件变成 4MB 字符串)。
我在前端使用 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)
时它工作正常。
但是我想要returnpdf文件。
有没有可能做到这一点?
谢谢。
单靠变异是做不到的。
您不能return file(binary)/headers(mime)/etc(需要由浏览器作为下载请求行为处理)使用json'ed 通信。 GraphQL 请求和响应采用 json 格式。
解决方案
工作流程:
- 调用突变(在变量中传递参数)
- 解析器创建文件内容并将其保存在某处(文件存储在服务器或 s3 上)
- 解析器 return 文件
id
是下载的一部分 url 或完整url
(字符串)到目标文件
id
或 url
在这种情况下应该是所需突变反应的一部分
mutation ($reportId: Int!) {
createPdf (reportId: $reportId){
reportDownloadId
}
}
- 突变结果(以及我们的
reportDownloadId
)在data
中可用
data
(result/response) from mutation 可以通过两种方式访问:
... 通过 { 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
中都有描述这两种方法都允许您(有条件地)呈现下载 button/link/component、f.e。
{downloadUrl && <DownloadReport url={downloadUrl}/>}
{downloadUrl && <a href={downloadUrl}>Report ready - download it</a>}
// render download, share, save in cloud, etc.
Handler 方法可用于自动调用下载文件请求,无需渲染额外按钮。这个 看起来像一个动作,而技术上有两个后续请求 (graphql 突变和下载)。这个
您可以将 PDF 编码为 Base64 并将其作为字符串发送。 然后在前端解码即可。
请注意,Base64 使用 4 个字节来编码每 3 个字节(3MB 文件变成 4MB 字符串)。