basic file upload works

This commit is contained in:
Sanj 2011-06-01 03:46:57 +05:30
parent 7fc6cd215f
commit bdf16adb52
9 changed files with 679 additions and 3 deletions

View File

@ -9,6 +9,8 @@ from convert import convertFile
from django.db.models.signals import post_save from django.db.models.signals import post_save
from settings import UPLOAD_ROOT, MEDIA_ROOT from settings import UPLOAD_ROOT, MEDIA_ROOT
from utils.add_file import hashFile, fileInfo, getFileType from utils.add_file import hashFile, fileInfo, getFileType
from django.core.files.base import ContentFile
#FIXME: The following two functions are ridiculous. please remove and clean up all references to them. #FIXME: The following two functions are ridiculous. please remove and clean up all references to them.
def baseFileName(filename): def baseFileName(filename):
@ -72,6 +74,7 @@ class File(models.Model):
file = models.FileField('File', upload_to=UPLOAD_ROOT, max_length=1024) file = models.FileField('File', upload_to=UPLOAD_ROOT, max_length=1024)
full_path = models.CharField(max_length=2048, blank=True, db_index=True) #makes it more efficient to retrieve a file by path instead of using self.file.path full_path = models.CharField(max_length=2048, blank=True, db_index=True) #makes it more efficient to retrieve a file by path instead of using self.file.path
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
done = models.BooleanField(default=False)
description = models.TextField(blank=True) description = models.TextField(blank=True)
tags = TagField("Tags", help_text="Enter as many tags as you like, separated by commas") tags = TagField("Tags", help_text="Enter as many tags as you like, separated by commas")
userID = models.ForeignKey(User) userID = models.ForeignKey(User)
@ -85,6 +88,21 @@ class File(models.Model):
# folder = models.ForeignKey("Folder") # folder = models.ForeignKey("Folder")
info = models.CharField(max_length=1024, blank=True, null=True) info = models.CharField(max_length=1024, blank=True, null=True)
def save_chunk(self, chunk, name='data.bin'):
if not self.done:
if not self.file:
self.file.save(name, ContentFile(chunk))
self.filename = name
self.save()
else:
f = open(self.file.path, 'a')
f.write(chunk)
f.close()
return True
return False
@classmethod @classmethod
def add_from_path(cls, category, user, path, **kwargs): def add_from_path(cls, category, user, path, **kwargs):
if cls.objects.filter(full_path=path).count() > 0: if cls.objects.filter(full_path=path).count() > 0:
@ -156,4 +174,4 @@ class Type(models.Model):
verbose_name = 'File Type' verbose_name = 'File Type'
verbose_name_plural = 'File Types' verbose_name_plural = 'File Types'
post_save.connect(convertFile, sender=File) # post_save.connect(convertFile, sender=File)

View File

@ -4,4 +4,15 @@ import views
urlpatterns = patterns('', urlpatterns = patterns('',
(r'addFolder', views.add_folder), (r'addFolder', views.add_folder),
(r'upload', views.upload_files), (r'upload', views.upload_files),
(r'add', views.add),
(r'^([A-Z0-9].*)/chunk$', views.chunk)
) )
# urlpatterns += patterns('views',
# (r'^$', 'index'),
# (r'^add$', 'add'),
# (r'^([A-Z0-9].*)/$', 'view'),
# (r'^([A-Z0-9].*)/chunk$', 'chunk'),
# (r'^([A-Z0-9].*)/(.+)$', 'download'),
# (r'^([A-Z0-9].*)$', 'view'),
# )

View File

@ -8,7 +8,9 @@ from settings import FTP_ROOT, UPLOAD_ROOT
import os import os
from os.path import join, isdir, getmtime, basename from os.path import join, isdir, getmtime, basename
import shutil import shutil
from django.views.decorators.csrf import csrf_exempt
from ox.django.shortcuts import render_to_json_response
from django.shortcuts import get_object_or_404
''' '''
class folder_names(object): class folder_names(object):
@ -85,6 +87,77 @@ def add_folder(request):
def editor(request): def editor(request):
return render_to_response("editor.html") return render_to_response("editor.html")
'''
def gallery(request): def gallery(request):
return HttpResponseRedirect('http://www.urbaanedge.com/wordpress') return HttpResponseRedirect('http://www.urbaanedge.com/wordpress')
'''
#Start Upload stuff - from hostb.org/host/views.py
class ChunkForm(forms.Form):
chunk = forms.FileField()
done = forms.IntegerField(required=False)
@csrf_exempt
def chunk(request, id):
if request.method == 'POST':
item = get_object_or_404(File, id=id)
form = ChunkForm(request.POST, request.FILES)
canEdit = True
if form.is_valid() and canEdit:
f = form.cleaned_data['chunk']
response = {
'resultUrl': '/files/' + str(item.id)
}
if item.title:
name = item.title
else:
name = f.name
if not item.save_chunk(f.read(), name):
response['result'] = 'failed'
elif form.cleaned_data['done']:
item.done = True
item.save()
response['done'] = 1
response['result'] = 1
else:
response['result'] = 1
return render_to_json_response(response)
response = {
'result': -1,
'fileUrl': '/'
}
return render_to_json_response(response)
@csrf_exempt
def add(request):
if request.method == 'POST':
if request.POST.get('firefogg', False):
file = File()
file.userID = request.user
file.info = '' #WTF!
file.title = request.POST.get('name', '')
file.save()
response = {
'result': 1,
'maxRetry': 10,
'uploadUrl': '/files/' + str(file.id) + "/chunk"
# 'uploadUrl': request.build_absolute_uri("%s/chunk" % file.get_absolute_url())
}
return render_to_json_response(response)
# Save any files that were uploaded (ignoring empty form fields)
if 'file' in request.FILES:
new_file = request.FILES['file']
file = File(title=new_file.name)
file.userID = request.user
file.done = True
file.info = '' #WTF!
file.file.save(new_file.name, new_file)
file.save()
if request.POST.get('api', False):
return HttpResponse(request.build_absolute_uri(file.get_absolute_url()), content_type="text/plain")
return HttpResponseRedirect(file.get_absolute_url())
#no upload
return HttpResponseRedirect('/')

View File

@ -4,6 +4,7 @@ from os.path import join
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
JSON_DEBUG = DEBUG
LOCAL_DEVELOPMENT = True LOCAL_DEVELOPMENT = True
APPEND_SLASH = True APPEND_SLASH = True
LOGGING_INTERCEPT_REDIRECTS = False LOGGING_INTERCEPT_REDIRECTS = False

View File

@ -0,0 +1,56 @@
$(document).ready(function() {
$('#firefogg').hide();
if(typeof(Firefogg) != 'undefined') {
$('#addFile').hide();
$('#firefogg').show();
$('#submitFile').hide();
ogg = new Firefogg();
$('#selectFile').click(function() {
if(ogg.selectVideo()) {
$('#selectFile').hide();
$('#submitFile').show();
}
});
$('#submitFile').click(function() {
$('#submitFile').hide();
$('#progressbar').show();
$('#progressbar').width(200);
$('#progressbar').css('background-color', '#eee');
$('#progressbar').html('<div id="progress" style="background-color: #666;height:20px;" /><div id="progressstatus" style="background-color: #fff;" />')
options = JSON.stringify({'passthrough': true});
var _data = $('#firefogg').serializeArray();
var data = {}
$(_data).each(function() {
data[this.name] = this.value;
})
data['firefogg'] = 1;
var data = JSON.stringify(data);
ogg.upload(options, add_url, data);
var updateStatus = function() {
var status = ogg.status();
var progress = ogg.progress();
//do something with status and progress, i.e. set progressbar width:
var progressbar = document.getElementById('progress');
progressbar.style.width= parseInt(progress*200) +'px';
$('#progressstatus').html(parseInt(progress*100) + '% - ' + status);
//loop to get new status if still encoding
if(ogg.state == 'encoding' || ogg.state == 'uploading') {
setTimeout(updateStatus, 500);
}
//encoding sucessfull, state can also be 'encoding failed'
else if (ogg.state == 'done') {
if(ogg.resultUrl)
document.location.href = ogg.resultUrl;
} else {
$('#progressstatus').html(ogg.state);
}
}
updateStatus();
});
}
});

View File

@ -0,0 +1,97 @@
/*
* jquery.uploadProgress
*
* Copyright (c) 2008 Piotr Sarnacki (drogomir.com)
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
*/
(function($) {
$.fn.uploadProgress = function(options) {
options = $.extend({
dataType: "json",
interval: 2000,
progressBar: "#progressbar",
progressUrl: "/progress",
start: function() {},
uploading: function() {},
complete: function() {},
success: function() {},
error: function() {},
preloadImages: [],
uploadProgressPath: '/javascripts/jquery.uploadProgress.js',
jqueryPath: '/javascripts/jquery.js',
timer: ""
}, options);
$(function() {
//preload images
for(var i = 0; i<options.preloadImages.length; i++)
{
options.preloadImages[i] = $("<img>").attr("src", options.preloadImages[i]);
}
/* tried to add iframe after submit (to not always load it) but it won't work.
safari can't get scripts properly while submitting files */
if($.browser.safari && top.document == document) {
/* iframe to send ajax requests in safari
thanks to Michele Finotto for idea */
iframe = document.createElement('iframe');
iframe.name = "progressFrame";
$(iframe).css({width: '0', height: '0', position: 'absolute', top: '-3000px'});
document.body.appendChild(iframe);
var d = iframe.contentWindow.document;
d.open();
/* weird - safari won't load scripts without this lines... */
d.write('<html><head></head><body></body></html>');
d.close();
var b = d.body;
var s = d.createElement('script');
s.src = options.jqueryPath;
/* must be sure that jquery is loaded */
s.onload = function() {
var s1 = d.createElement('script');
s1.src = options.uploadProgressPath;
b.appendChild(s1);
}
b.appendChild(s);
}
});
return this.each(function(){
$(this).bind('submit', function() {
var uuid = "";
for (i = 0; i < 32; i++) { uuid += Math.floor(Math.random() * 16).toString(16); }
/* update uuid */
options.uuid = uuid;
/* start callback */
options.start();
/* patch the form-action tag to include the progress-id if X-Progress-ID has been already added just replace it */
if(old_id = /X-Progress-ID=([^&]+)/.exec($(this).attr("action"))) {
var action = $(this).attr("action").replace(old_id[1], uuid);
$(this).attr("action", action);
} else {
$(this).attr("action", jQuery(this).attr("action") + "?X-Progress-ID=" + uuid);
}
var uploadProgress = $.browser.safari ? progressFrame.jQuery.uploadProgress : jQuery.uploadProgress;
options.timer = window.setInterval(function() { uploadProgress(this, options) }, options.interval);
});
});
};
jQuery.uploadProgress = function(e, options) {
jQuery.ajax({
type: "GET",
url: options.progressUrl + "?X-Progress-ID=" + options.uuid,
dataType: options.dataType,
success: function(upload) {
options.uploading(upload);
}
});
};
})(jQuery);

View File

@ -0,0 +1,119 @@
/*
* based on jquery.uploadProgress
*
* Copyright (c) 2008 Piotr Sarnacki (drogomir.com)
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
*/
(function($) {
$.fn.uploadProgress = function(options) {
options = $.extend({
dataType: "json",
interval: 2000,
progressBar: "#progressbar",
progressUrl: "/progress",
start: function() {},
uploading: function() {},
complete: function() {},
success: function() {},
error: function() {},
preloadImages: [],
uploadProgressPath: '/javascripts/jquery.uploadProgress.js',
jqueryPath: '/javascripts/jquery.js',
timer: ""
}, options);
$(function() {
//preload images
for(var i = 0; i<options.preloadImages.length; i++)
{
options.preloadImages[i] = $("<img>").attr("src", options.preloadImages[i]);
}
/* tried to add iframe after submit (to not always load it) but it won't work.
safari can't get scripts properly while submitting files */
if($.browser.safari && top.document == document) {
/* iframe to send ajax requests in safari
thanks to Michele Finotto for idea */
iframe = document.createElement('iframe');
iframe.name = "progressFrame";
$(iframe).css({width: '0', height: '0', position: 'absolute', top: '-3000px'});
document.body.appendChild(iframe);
var d = iframe.contentWindow.document;
d.open();
/* weird - safari won't load scripts without this lines... */
d.write('<html><head></head><body></body></html>');
d.close();
var b = d.body;
var s = d.createElement('script');
s.src = options.jqueryPath;
/* must be sure that jquery is loaded */
s.onload = function() {
var s1 = d.createElement('script');
s1.src = options.uploadProgressPath;
b.appendChild(s1);
}
b.appendChild(s);
}
});
return this.each(function(){
$(this).bind('submit', function() {
var uuid = "";
for (i = 0; i < 32; i++) { uuid += Math.floor(Math.random() * 16).toString(16); }
/* update uuid */
options.uuid = uuid;
/* start callback */
options.start();
/* patch the form-action tag to include the progress-id if X-Progress-ID has been already added just replace it */
if(old_id = /X-Progress-ID=([^&]+)/.exec($(this).attr("action"))) {
var action = $(this).attr("action").replace(old_id[1], uuid);
$(this).attr("action", action);
} else {
$(this).attr("action", jQuery(this).attr("action") + "?X-Progress-ID=" + uuid);
}
var uploadProgress = $.browser.safari ? progressFrame.jQuery.uploadProgress : jQuery.uploadProgress;
options.timer = window.setInterval(function() { uploadProgress(this, options) }, options.interval);
});
});
};
jQuery.uploadProgress = function(e, options) {
jQuery.ajax({
type: "GET",
url: options.progressUrl + "?X-Progress-ID=" + options.uuid,
dataType: options.dataType,
success: function(upload) {
options.uploading(upload);
}
});
};
})(jQuery);
$(document).ready(function() {
$('#addFile').uploadProgress({
/* scripts locations for safari */
jqueryPath: "/static/js/jquery.js",
uploadProgressPath: "/static/js/upload/jquery.uploadProgress.js",
start: function(upload) {
$('#addFile').hide();
$('#progressbar').show();
$('#progressbar').html('...');
},
uploading: function(upload) {
var filename = $('#file').val();
filename = filename.split('/');
filename = filename[filename.length-1];
$('#progressbar').html(upload.progressbar_html);
if(upload.precents)
document.title = upload.precents + "% of " + filename;
},
interval: 2500
});
});

View File

@ -0,0 +1,301 @@
// -*- coding: utf-8 -*-
// vi:si:et:sw=2:sts=2:ts=2
// Workaround for Firefox 4.0 sending an empty string
// as filename for Blobs in FormData requests
// https://bugzilla.mozilla.org/show_bug.cgi?id=649150
function geckoFormData() {
var self = this,
that = {},
boundary = '------XX' + Math.random(),
dashdash = '--',
crlf = '\r\n',
builder = '', // Build RFC2388 string.
wait = 0;
builder += dashdash + boundary + crlf;
that.append = function(name, data) {
// Generate headers.
builder += 'Content-Disposition: form-data; name="'+ name +'"';
builder += crlf;
builder += crlf;
// Write data.
builder += data;
builder += crlf;
// Write boundary.
builder += dashdash + boundary + crlf;
};
that.appendFile = function(name, data, type, filename) {
builder += 'Content-Disposition: form-data; name="'+ name +'"';
builder += '; filename="' + filename + '"';
builder += crlf;
builder += 'Content-Type: ' + type;
builder += crlf;
builder += crlf;
// Write binary data.
builder += data;
builder += crlf;
// Write boundary.
builder += dashdash + boundary + crlf;
};
that.appendBlob = function(name, blob, filename) {
wait++;
var reader = new FileReader();
reader.onload = function(e) {
that.appendFile(name, e.target.result, blob.type, filename);
// Call onload after last Blob
wait--;
if(!wait && self.onload) {
self.onload();
}
};
reader.readAsBinaryString(blob);
};
that.send = function(xhr) {
self.onload = function() {
// Mark end of the request.
builder += dashdash + boundary + dashdash + crlf;
// Send to server
xhr.setRequestHeader("Content-type", "multipart/form-data; boundary=" + boundary);
xhr.sendAsBinary(builder);
};
if(!wait) {
self.onload();
}
};
return that;
}
function FirefoggUploader(file, uploadUrl, uploadData, callback, progress_callback) {
var self = this,
that = {
progress: 0
},
chunkSize = 1024*1024,
chunkUrl,
gecko = /gecko\//.test(navigator.userAgent.toLowerCase()),
maxRetry = -1,
progress,
retries = 0;
progress_callback = progress_callback || function(progress) {};
callback = callback || function(result) {};
function uploadChunk(chunkId) {
var bytesAvailable = file.size,
chunk,
chunkOffset = chunkId * chunkSize;
//Slice API was taken out and new version now require start/end and not start/length
if(file.slice)
chunk = file.slice(chunkOffset, chunkSize, file.type);
else if(file.mozSlice)
chunk = file.mozSlice(chunkOffset, chunkOffset+chunkSize, file.type);
else if(file.webkitSlice)
chunk = file.webkitSlice(chunkOffset, chunkOffset+chunkSize, file.type);
that.progress = parseFloat(chunkOffset)/bytesAvailable;
self.req = new XMLHttpRequest();
self.req.addEventListener("load", function (evt) {
var response;
that.responseText = evt.target.responseText;
try {
response = JSON.parse(evt.target.responseText);
} catch(e) {
response = {};
}
if (response.done == 1) {
//upload finished
that.resultUrl = response.resultUrl;
that.progress = 1;
that.status = 'done';
callback(that);
}
else if (response.result == 1) {
//reset retry counter
retries = 0;
//start uploading next chunk
uploadChunk(chunkId + 1);
} else {
//failed to upload, try again in 3 second
retries++;
if (maxRetry > 0 && retries > maxRetry) {
that.status = 'uplaod failed';
that.progress = -1;
callback(that);
} else {
setTimeout(function() {
uploadChunk(chunkId);
}, 3000);
}
}
}, false);
self.req.addEventListener("error", function (evt) {
//failed to upload, try again in 3 second
retries++;
if (maxRetry > 0 && retries > maxRetry) {
that.status = 'uplaod failed';
that.progress = -1;
callback(that);
} else {
setTimeout(function() {
uploadChunk(chunkId);
}, 3000);
}
}, false);
self.req.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
that.progress = parseFloat(chunkOffset + evt.loaded) / bytesAvailable;
progress_callback(that.progress);
}
}, false);
self.req.addEventListener("abort", function (evt) {
that.status = 'aborted';
that.progress = -1;
callback(that);
}, false);
var formData;
if(gecko) {
formData = new geckoFormData();
} else {
formData = new FormData();
}
formData.append('chunkId', chunkId);
if (bytesAvailable <= chunkOffset + chunkSize) {
formData.append('done', 1);
}
if(gecko) {
formData.appendBlob('chunk', chunk, 'chunk.bin');
} else {
formData.append('chunk', chunk);
}
self.req.open("POST", chunkUrl, true);
if(gecko) {
formData.send(self.req);
} else {
self.req.send(formData);
}
}
//request upload slot from server
that.status = 'requesting chunk upload';
self.req = new XMLHttpRequest();
self.req.addEventListener("load", function (evt) {
var response = {};
that.responseText = evt.target.responseText;
try {
response = JSON.parse(evt.target.responseText);
} catch(e) {
response = {};
that.status = "failed to parse response";
that.progress = -1;
callback(that);
}
if (response.maxRetry) {
maxRetry = response.maxRetry;
}
chunkUrl = response.uploadUrl;
if (chunkUrl) {
that.status = 'uploading';
that.progress = 0.0;
//start upload
uploadChunk(0);
} else {
that.status = 'upload failed, no upload url provided';
that.progress = -1;
callback(that);
}
}, false);
self.req.addEventListener("error", function (evt) {
that.status = 'uplaod failed';
that.progress = -1;
that.responseText = evt.target.responseText;
callback(that);
}, false);
self.req.addEventListener("abort", function (evt) {
that.status = 'aborted';
that.progress = -1;
callback(that);
}, false);
var formData = new FormData();
for(var key in uploadData) {
if (uploadData.hasOwnProperty(key)) {
formData.append(key, uploadData[key]);
}
}
self.req.open("POST", uploadUrl);
self.req.send(formData);
//public interface
that.abort = function() {
if (self.req) {
self.req.abort();
self.req = null;
}
};
return that;
}
// Activate on page is possible
$(document).ready(function() {
var Chrome = /chrome/.test(navigator.userAgent.toLowerCase()),
GeckoVersion = navigator.userAgent.match(/rv\:(.+)\) Gecko/),
WebKitVersion = navigator.userAgent.match(/AppleWebKit\/([\d\.]+)/);
if (WebKitVersion) {
WebKitVersion = WebKitVersion[1];
}
if (GeckoVersion) {
GeckoVersion = GeckoVersion[1];
}
if(Chrome || WebKitVersion >= '534.28' ||
(GeckoVersion >= '2.0')) {
$('#firefogg').hide();
$('#submit').hide();
$('#file').change(function(e) {
if(this.files.length>0) {
$('#file').hide();
$('#upload').show();
}
});
if($('#file')[0].files.length>0) {
$('#file').hide();
$('#upload').show();
} else {
$('#upload').hide();
}
$('#upload').click(function(e) {
$('#upload').hide();
e.stopPropagation();
var file = $('#file')[0].files[0];
var data = {
'firefogg': 1,
'name': file.name // Send filename here since Blobs will have no name later
};
var ogg = FirefoggUploader(file, add_url, data, function(ogg) { //callback
if(ogg.resultUrl) {
document.location.href = ogg.resultUrl;
} else {
$('#progressstatus').html(ogg.status);
}
}, function(progress) { //progress callback
//do something with status and progress, i.e. set progressbar width:
$('#progress').css('width', parseInt(progress*100, 10) +'%');
$('#progressstatus').html(parseInt(progress*100, 10) + '% - ' + ogg.status);
});
$('#submitFile').hide();
$('#progressbar').show();
$('#progressbar').width(200);
$('#progressbar').css('background-color', '#eee');
$('#progressbar').html('<div id="progress" style="background-color: #666;height:20px;width:0%" /><div id="progressstatus" style="background-color: #fff;">uploading</div>');
});
}
});

View File

@ -26,7 +26,7 @@ urlpatterns = patterns('',
(r'^comments/', include('django.contrib.comments.urls')), (r'^comments/', include('django.contrib.comments.urls')),
(r'^edit/', include('editor.urls')), (r'^edit/', include('editor.urls')),
(r'files/', include('files.urls')), (r'files/', include('files.urls')),
(r'^gallery/', 'files.views.gallery'), # (r'^gallery/', 'files.views.gallery'),
# Uncomment the next line to enable the admin: # Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)), (r'^admin/', include(admin.site.urls)),
(r'^accounts/login/$', 'django.contrib.auth.views.login'), (r'^accounts/login/$', 'django.contrib.auth.views.login'),