pages tagged pluploadFeeding the Cloudhttps://feeding.cloud.geek.nz/tags/plupload/Feeding the Cloudikiwiki2021-06-11T20:43:57ZUsing plupload inside a Django applicationhttps://feeding.cloud.geek.nz/posts/using-plupload-inside-django/
<a href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>
2021-06-11T20:43:57Z2011-04-07T20:00:00Z
<p><a href="http://plupload.org/">Plupload</a> is a reusable component which fully takes advantage of the file upload capabilities of your browser and its plugins. It will use HTML5, Flash and Google Gears if they are available, but otherwise, it can gracefully degrade down to HTML4 it it needs to. Here's how it can be integrated within a Django web application.</p>
<p>(I have posted the <a href="https://github.com/fmarier/plupload_django">sample application</a> I will refer to and you may use it <a href="https://github.com/fmarier/plupload_django/blob/master/COPYING.txt">anyway you like</a>.)</p>
<h3 id="Creating_a_basic_upload_form">Creating a basic upload form</h3>
<p>The first step is to create a simple one-file upload form that will be used in the case where Javascript is disabled:</p>
<pre><code>class UploadForm(forms.Form):
file = forms.FileField()
def save(self, uploaded_file):
print 'File "%s" would presumably be saved to disk now.' % uploaded_file
pass
</code></pre>
<p>Then you can add this form to one of your templates:</p>
<pre><code><form enctype="multipart/form-data" action="{% url plupload_sample.upload.views.upload_file %}" method="post">
{% csrf_token %}
<div id="uploader">
{{form.file.errors}}{{form.file}}
<input type="submit" value="Upload" />
</div>
</form>
</code></pre>
<p>And create a new method to receive the form data:</p>
<pre><code>@csrf_protect
def upload_file(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = request.FILES['file']
form.save(uploaded_file)
return HttpResponseRedirect(reverse('plupload_sample.upload.views.upload_file'))
else:
form = UploadForm()
return render_to_response('upload_file.html', {'form': form}, context_instance=RequestContext(request))
</code></pre>
<h3 id="Adding_Plupload_to_the_template">Adding Plupload to the template</h3>
<p>In order to display the right Javascript-based upload form, add the following code, based on the <a href="http://plupload.org/example_queuewidget.php">official example</a>, to the head of your template:</p>
<pre><code><link rel="stylesheet" href="/css/plupload.queue.css" type="text/css">
<script type="text/javascript" src="/js/jquery.min.js"></script>
<script type="text/javascript" src="/js/plupload.full.min.js"></script>
<script type="text/javascript" src="/js/jquery.plupload.queue.min.js"></script>
<script type="text/javascript">
$(function() {
$("#uploader").pluploadQueue({
runtimes : 'html5,html4',
url : '{% url plupload_sample.upload.views.upload_file %}',
max_file_size : '1mb',
chunk_size: '1mb',
unique_names : false,
multipart: true,
headers : {'X-Requested-With' : 'XMLHttpRequest', 'X-CSRFToken' : '{{csrf_token}}'},
});
$('form').submit(function(e) {
var uploader = $('#uploader').pluploadQueue();
// Validate number of uploaded files
if (uploader.total.uploaded == 0) {
// Files in queue upload them first
if (uploader.files.length > 0) {
// When all files are uploaded submit form
uploader.bind('UploadProgress', function() {
if (uploader.total.uploaded == uploader.files.length)
$('form').submit();
});
uploader.start();
} else {
alert('You must at least upload one file.');
}
e.preventDefault();
}
});
});
</script>
</code></pre>
<p>Pay close attention to the extra headers that need to be added to ensure that the AJAX requests will pass the <a href="http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/">Django CSRF checks</a>:</p>
<pre><code>X-Requested-With: XMLHttpRequest
X-CSRFToken: {{csrf_token}}
</code></pre>
<h3 id="Adding_Plupload_to_the_view_method">Adding Plupload to the view method</h3>
<p>Now in order to properly receive the files uploaded by Plupload via AJAX calls, we need to revise our upload_file() method:</p>
<pre>
@csrf_protect
def upload_file(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = request.FILES['file']
form.save(uploaded_file)
<b>if request.is_ajax():
response = HttpResponse('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}', mimetype='text/plain; charset=UTF-8')
response['Expires'] = 'Mon, 1 Jan 2000 01:00:00 GMT'
response['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
response['Pragma'] = 'no-cache'
return response
else:
return HttpResponseRedirect(reverse('plupload_sample.upload.views.upload_file'))</b>
else:
form = UploadForm()
return render_to_response('upload_file.html', {'form': form}, context_instance=RequestContext(request))
</pre>
<p>The above includes the response (which is not really documented as far as I can tell) that needs to be sent back to Plupload to make sure it knows that the file has been received successfully:</p>
<pre><code>{"jsonrpc" : "2.0", "result" : null, "id" : "id"}
</code></pre>
<h3 id="Adding_support_for_multipart_files">Adding support for multipart files</h3>
<p>Our solution so far works fine except when uploading large files that need to be sent in multiple chunks.</p>
<p>This involves writing to a temporary file until all parts have been received:</p>
<pre>
@csrf_protect
def upload_file(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = request.FILES['file']
<b>chunk = request.REQUEST.get('chunk', '0')
chunks = request.REQUEST.get('chunks', '0')
name = request.REQUEST.get('name', '')
if not name:
name = uploaded_file.name
temp_file = '/tmp/insecure.tmp'
with open(temp_file, ('wb' if chunk == '0' else 'ab')) as f:
for content in uploaded_file.chunks():
f.write(content)
if int(chunk) + 1 >= int(chunks):
form.save(temp_file, name)</b>
if request.is_ajax():
response = HttpResponse('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}', mimetype='text/plain; charset=UTF-8')
response['Expires'] = 'Mon, 1 Jan 2000 01:00:00 GMT'
response['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
response['Pragma'] = 'no-cache'
return response
else:
return HttpResponseRedirect(reverse('plupload_sample.upload.views.upload_file'))
else:
form = UploadForm()
return render_to_response('upload_file.html', {'form': form}, context_instance=RequestContext(request))
</pre>
<p>Note that I have used <code>/tmp/insecure.tmp</code> for brevity. In a real application, you do need to use a <a href="http://docs.python.org/library/tempfile.html">secure mechanism</a> to create the temporary file or you would expose yourself to a <a href="https://duckduckgo.com/?q=insecure+temp+file+creation">tempfile vulnerability</a>.</p>