[Seaside] Using nginx file upload module

Nick Ager nick.ager at gmail.com
Thu Jun 16 08:13:31 UTC 2011


Hi Johan,

> But I'm looking forward to reading your write-up too!
>

For what it's worth I started writing it up here:
http://www.nickager.com/blog/file-upload-using-Nginx-and-Seaside - though
it's little more than notes at the moment..

In the meantime here is a raw dump of some code. Hope it might help, though
I'm afraid it's more complex than it needs to be as it includes upload
tracking, Iframe submission, additional user feedback.

I set the target of the form to a hidden Iframe:

form := html form.

form

id: #fileUploadForm;

style: 'margin:0px';

attributeAt: 'target' put: 'hiddenImageUploader';

onSubmit: 'startUploadProgressBar(); return true;';


I manipulate the path using javascript:

fileuploadSpecificJavascript

^ String streamContents: [:aStream |

aStream

nextPutAll: 'var xProgressId="';

nextPutAll:  self xProgressId;

nextPutAll: '";

function checkFileAndSubmitIfImage(actionUrl) {

var filename = $("#fileUploadId").val();

var extensionIndex=filename.lastIndexOf(''.'') + 1;

var extension=filename.substring(extensionIndex).toLowerCase();

 if (extension && /^(jpg|png|jpeg|gif)$/.test(extension)) {

var newUrl = "/fileupload" + actionUrl+"&X-Progress-ID=" + xProgressId;

$("#fileUploadForm").attr("action", newUrl);

$("#fileUploadForm").submit();

} else {

$("#uploadNotification").append("<ul class=\"errors\"><li>''" + filename +
> "'' is not a valid image file (jpg, jpeg, gif, png), try another
> file</li></ul>");

}


> $("#fileUploadId").val("");

return false;

}

' ]


You trigger an update with the iframe as a target. The image is uploaded and
a minimal response is returned to the iframe. That response triggers the
onload. The onload then calls a method in the iframes parent - the main
page.   I've called it imageUploadedCallback() then performs the following:
1) clears the upload progress
2) renders any upload errors
3) renders thumb nails of the images uploaded
4) sets a new xProgressId for the next upload.

The imageUploadedCallback()  code looks like:

imageUploadedCallbackScriptOn: html
^ html jQuery ajax script: [ :s |
s << (JSStream on: 'clearUploadProgress()').
 s << (s jQuery: #uploadNotification)
html: [ :renderer |
errorText notNil
ifTrue: [
renderer div
class: 'error';
with: errorText ]
ifFalse: [
renderer div] ].
s << (s jQuery: #projectUploadedFilesContainer)
html: [ :renderer |
renderer render: self imageRenderer ].
 s << (JSStream on: 'xProgressId="', self xProgressId, '"') ]

 with the form I also render a hiddenInput, whose callback is fired when the
form is submitted and I extract the nginx post parameters which tell me the
location nginx has stored the file:

"The hiddenInput callback will contain the file upload fields if we've
> loading via nginx

file upload module"

html hiddenInput

value: 'hidden';

callback: [:val | | request postFields fileName uploadFieldName |

request := self requestContext request.

postFields := request postFields.

uploadFieldName := fileUploadField attributeAt: 'name'.

fileName := (postFields at: (uploadFieldName, '.name') ifAbsent: [nil]).

"has nginx file upload module inserted it's post fields in the request?"

fileName notNil ifTrue: [

fileMoveCallback value: postFields value: uploadFieldName ] ]


I use the OSes 'mv' to move the files:

moveFrom: fromString to: toString

| shellMoveFileCommand |

shellMoveFileCommand := String streamContents: [:stream |

stream

nextPutAll: 'mv ';

nextPutAll: fromString;

nextPutAll: ' ''';

nextPutAll: toString;

nextPutAll: '''' ].

IZLogger logInfo: 'IZShellScripts>>#moveFrom:to:' with:
> shellMoveFileCommand.

SpEnvironment runShellCommandString: shellMoveFileCommand.


some more javascript:

imageUploadedCallbackScriptOn: html

^ html jQuery ajax script: [ :s |

s << (JSStream on: 'clearUploadProgress()').

 s << (s jQuery: #uploadNotification)

html: [ :renderer |

errorText notNil

ifTrue: [

renderer div

class: 'error';

with: errorText ]

ifFalse: [

renderer div] ].

s << (s jQuery: #projectUploadedFilesContainer)

html: [ :renderer |

renderer render: self imageRenderer ].

 s << (JSStream on: 'xProgressId="', self xProgressId, '"') ]


and one more javascript method:

startUploadProgressBarJavascript

^'

var interval = null;


> function startUploadProgressBar() {

$("#progressBar").progressbar({ value: 0 });

$("#progressBar").css("display", "block");

$("#fileUploadContainer").css("display", "none");

interval = window.setInterval( function () {  fetch(xProgressId);  }, 1000);

}


> function clearUploadProgress() {

window.clearTimeout(interval);

$("#progressBar").css("display", "none");

$("#fileUploadContainer").css("display", "block");

}


> function setUploadStatus (statusText) {

$("#uploadNotification").html("<div class=''status''>" + statusText +
> "</div>");

}


function fetch(uuid) {

$.ajax({

url: "/progress",

beforeSend: function (xhr) {

xhr.setRequestHeader("X-Progress-ID", uuid);

},

success: function (data, textStatus, xhr) {

var upload = eval(data);


>          if (upload.state == "done" || upload.state == "uploading") {

var percentageComplete = Math.floor(upload.received * 100 / upload.size);

$("#progressBar").progressbar({ value: percentageComplete });

if (upload.received != upload.size) {

setUploadStatus("Uploading: " + percentageComplete + "%");

} else {

setUploadStatus("Uploaded: processing");

}

         }

if (upload.state == "starting") {

setUploadStatus("Starting upload");

}

         if (upload.state == "done") {

           clearUploadProgress();

         }

},

error: function(xhr, textError) {

setUploadStatus("Error: " + textError);

}

});

}

'
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/seaside/attachments/20110616/2f8f9a12/attachment-0001.htm


More information about the seaside mailing list