I have now found a solution that works for this problem. My upload now works as follows:
I read the file as a DataURL into a FileReader
I slice the returned string up and send each slice up to the server
where it is stored in a session variable
Once the whole file has been sent up I call another web service which
glues the slices back together and turns the result into a byte array
The byte array is then stored as a file in local storage in azure
Finally the file is transferred from local storage into blob
storage
It’s probably not the best way to do it, but it seems to work (in browsers that support html5). If anyone has suggestions for improvements, please let me know. I had to play around with the maxSliceSize to get it to work and the largest that I could get it to was 256 * 32.
Thanks to:
http://gauravmantri.com/2013/02/16/uploading-large-files-in-windows-azure-blob-storage-using-shared-access-signature-html-and-javascript/
How to convert image to byte array using javascript only to store image on sql server?
http://www.west-wind.com/weblog/posts/2009/Sep/15/Making-jQuery-calls-to-WCFASMX-with-a-ServiceProxy-Client
Code as follows (I've cut out bits of code that were only relevant to my project so hope what's left makes sense):
var reader;
var filename;
var sContainer;
var maxSliceSize = 256 * 32;
var selectedFile = null;
var sliceIds = new Array();
var upFile;
function handleFileUpload(cnt, sType) {
var files = cnt.files; // FileList object
selectedFile = files[0];
//----------------------------CHECKS---------------------------
//Check whether there is a file to upload
if (files.length === 0) { return; }
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
} else {
alert('The File APIs are not fully supported in this browser.');
return;
}
//test whether this is an image file
rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;
if (!rFilter.test(selectedFile.type)) { alert("You must select a valid image file!"); return; }
//----------------------------UPLOAD---------------------------
//Create a name for the blob
filename = selectedFile.name.toLowerCase();
sContainer = "images"
//Upload the file
reader = new FileReader();
reader.onloadend = function (evt) {
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
//Initialise variables
maxSliceSize = 256 * 32;
upFile = evt.target.result
sliceIds = new Array();
uploadFileInSlices();
}
}
reader.readAsDataURL(selectedFile);
}
function pad(number, length) {
var str = '' + number;
while (str.length < length) {
str = '0' + str;
}
return str;
}
function uploadFileInSlices() {
if (upFile != "") {
var sliceId = pad(sliceIds.length, 6);
console.log("slice id = " + sliceId);
sliceIds.push(sliceId);
//Send the first slice off to the server and remove it from the file string
var upSlice = upFile.substring(0, maxSliceSize);
upFile = upFile.substring(maxSliceSize);
var params = {
filename: filename,
sliceID: sliceId,
upSlice: upSlice
};
proxy.invoke("UploadImageSlice", params, uploadFileInSlices, onProxyFailure, true);
} else {
commitSliceList();
}
}
function commitSliceList() {
var jsonData = []; //declare object
for (var i = 0; i < sliceIds.length; i++) {
jsonData.push({ SliceName: sliceIds[i] });
}
console.log(jsonData);
var params = {
filename: filename,
sliceList: jsonData,
upFileType: selectedFile.type,
sContainer: sContainer
};
proxy.invoke("UploadImage", params, onSuccess, onProxyFailure, true);
}
Web services (these aren’t complete but should give the basic idea):
<OperationContract()>
Public Function UploadImageSlice(ByVal blobFileName As String, ByVal sliceID As String, ByVal upSlice As String, iInspection As Integer) As Boolean
HttpContext.Current.Session(blobFileName & sliceID) = upSlice
Return true
End Function
<OperationContract()>
Public Function UploadImage(ByVal blobFileName As String,ByVal sliceList As List(Of SliceList), upFileType As String, ByVal sContainer As String) As Boolean
'Find the root path for local storage
Dim sRoot As String = ""
Dim myReportsStorage As LocalResource = RoleEnvironment.GetLocalResource("myReports")
sRoot = myReportsStorage.RootPath
'Check whether the file already exists in local storage
If My.Computer.FileSystem.FileExists(sRoot & blobFileName) Then
My.Computer.FileSystem.DeleteFile(sRoot & blobFileName)
End If
‘GlueUploadSlices pulls strings out of session variables and sticks them together
Dim upFile As String = GlueUploadSlices(blobFileName, sliceList)
Dim upFileByte As [Byte]() = New [Byte](upFile.Length - 1) {}
'FixBase64ForImage extracts the appropriate string from upFile
upFileByte = Convert.FromBase64String(FixBase64ForImage(upFile))
Using fs As FileStream = File.OpenWrite(sRoot & blobFileName)
fs.Write(upFileByte, 0, upFileByte.Length)
fs.Close()
End Using
'Save file to local storage
StoreBlob(sContainer, sRoot, blobFileName)
Return true
End Function