我有一个芹菜任务,只需单击一个按钮即可创建PDF文件。
单击按钮时,javascript端将继续检查直到任务完成为止;当任务完成时,它将下载文件。我想一些递归可以做到:
$(".getPdf").on('click', function(event) {
// Grab the clicked object ID and the filename stored on the data attributes
var thisId = $(this).data('runid');
var filename = $(this).data('filename');
var taskID = "None";
var pdfStatus = "None";
// Run download function
downloadPdf(filename, taskID, pdfStatus);
function downloadPdf(filename, taskID, pdfStatus) {
// Send a POST request to Django's RunsView with the Run ID, pdf filename, task ID and pdfStatus (if any)
$.post({
url: "{% url 'runs' %}",
data: {
csrfmiddlewaretoken: "{{ csrf_token }}",
id: thisId,
filename: filename,
task_id: taskID,
},
success: function(data) {
// Split the returned string to separate the task ID [0] from the status [1]
var taskID = data.split(";")[0];
var pdfStatus = data.split(";")[1];
// Convert the pdfStatus Python bools into JavaScript bools
if (pdfStatus == "False") {
pdfStatus = false;
} else if (pdfStatus == "True") {
pdfStatus = true;
};
if (!pdfStatus) {
console.log("Repeat function.");
downloadPdf(filename, taskID, pdfStatus);
} else {
console.log("Download PDF");
window.open("data:application/pdf;base64," + data);
};
},
traditional: true
}).done();
};
});
PDF下载的实际JavaScript方面非常简单。我发送要生成PDF的对象的ID(“ 运行”)以及该文件应具有的文件名(该文件名在 get 上生成)该页面的Django视图的一部分),以及一个空的celery任务ID(当然,该任务尚未创建)。我得到的响应是由“ celery task ID”组成的字符串;如果尚未完成任务,则为False,因此我重复POST请求。
在后端,我根据任务ID是否存在来处理POST请求:
def post(self, request):
# Get the args from the incoming request
run_id = request.POST.get('id')
filename = request.POST.get('filename')
task_id = request.POST.get('task_id')
if run_id and task_id == "None":
# Database and Django stuff happens here for a few lines...
# Fire off a Celery task to generate the PDF file asynchronously
pdf_task = create_pdf.delay(sensor, range_period)
response_string = str(pdf_task.task_id) + ";" + str(AsyncResult(pdf_task.task_id).ready())
return HttpResponse(response_string)
elif task_id != "None":
pdf_done = AsyncResult(task_id).ready() # If this is false, send the task ID back with a False
if not pdf_done:
response_string = str(task_id) + ";" + str(pdf_done)
return HttpResponse(response_string)
# Otherwise send the PDF back
else:
pdf_task = AsyncResult(task_id)
pdf_file = pdf_task.result
pdf_file_location = settings.MEDIA_ROOT + '/reports/' + pdf_file
# response = HttpResponse(content_type='application/pdf')
# response['Content-Disposition'] = 'attachment; filename={}'.format(pdf_task.result)
# # return response
try:
# with open(pdf_file_location, 'r') as pdf:
# response = HttpResponse(pdf.read(), content_type='application/pdf')
# response['Content-Disposition'] = 'attachment; filename="' + pdf_task.result + '"'
# pdf.close()
return FileResponse(open(pdf_file_location, 'rb'), content_type='application/pdf')
except IOError:
print("File does not exist, check above.")
return response
我一直在努力使文件下载与AJAX请求一起工作。到目前为止,没有被注释掉的代码实际上已经将一串编码文本返回到前端,但是我不确定如何解码,最后得到一个看起来像这样的标签:
因此,在完成文件处理之后,编码的PDF到达了前端(那很好),但是我对JavaScript的了解还不够,无法弄清楚如何将编码的字符串转换为PDF文件。
答案 0 :(得分:0)
您可以将base64解码为Blob并将其下载为文件。
import React, { Component } from 'react';
import {FaUpload} from 'react-icons/fa';
import Dropzone from 'react-dropzone';
import ImagePreview from './ImagePreview.js';
const baseStyle = {
width: "100%",
height: 400,
borderWidth: 2,
borderColor: '#808080',
borderStyle: 'dashed',
backgroundColor: "#dcdcdc",
borderRadius: 5
};
const activeStyle = {
borderStyle: 'solid',
borderColor: '#6c6',
backgroundColor: '#eee'
};
const rejectStyle = {
borderStyle: 'solid',
borderColor: '#c66',
backgroundColor: '#eee'
};
class FileUploader extends Component{
constructor(props){
super(props);
this.removeImage = this.removeImage.bind(this);
let files = [];
if(props.fileList !== null && props.fileList !== undefined){
files = props.fileList;
}
this.state = {uploadedImages: files};
}
onDrop = (acceptedFiles, rejectedFiles) =>{
let arr = this.state.uploadedImages;
acceptedFiles.forEach(file => {
arr.push(file);
});
this.setState({uploadedImages: arr},function(){
this.props.onUpload(this.state.uploadedImages);
});
}
getPreviews(){
if(this.state.uploadedImages === null || this.state.uploadedImages ===
undefined){
return (
<div>
<ImagePreview width="100px" height="100px"/>
{/*<div style={emptyBox}/>*/}
</div>);
}else{
const previews = this.state.uploadedImages.map(function(img, index) {
return <ImagePreview img={URL.createObjectURL(img)} key={index}
width="100px" height="100px" onClick={() =>this.removeImage(index)}/>;
}.bind(this));
return (
<div>
{previews}
<ImagePreview width="100px" height="100px"/>
</div>
);
}
}
removeImage(index){
let arr = this.state.uploadedImages;
arr.splice(index,1);
this.setState({uploadedImages: arr});
}
render(){
return(
<div>
<Dropzone accept="image/*" onDrop={this.onDrop.bind(this)}>
{({ getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles, rejectedFiles }) => {
let styles = {...baseStyle}
styles = isDragActive ? {...styles, ...activeStyle} : styles
styles = isDragReject ? {...styles, ...rejectStyle} : styles
return (
<div {...getRootProps()} style={styles}>
<input {...getInputProps()} />
<div style={{margin: "auto", textAlign: "center", position: "relative", top: "30%"}}>
<FaUpload size="100"/><br/>
{!isDragReject &&<strong>Drag and drop or click here</strong>}
{isDragReject && <strong>You can only upload Images</strong>}
</div>
</div>
)
}}
</Dropzone>
<div style={{marginTop: "10px", border: "1px solid gray", padding: "5px"}}>
{this.getPreviews()}
</div>
</div>
);
}
}
export default FileUploader;