Monday, August 20, 2018

Using PLUpload with ASP.Net

It's a fairly common use case scenario in which one must allow their users to upload files into a web system for various means. Of course, in the situations I find myself, I need to allow this to happen, and I don't necessarily have a maximum size allowed for the file to be processed.  It's been a while since I last looked into uploading files, and even then I just used the traditional Input Type='File' control that's a part of the standard suite of HTML4 form elements.

Of course I had to push up the number of bytes that were allowed up at any given time, but it was still the simple file upload control. 

Fast forward a few years, and I knew there had to be a better solution. Or at least I hoped there was. 

Prayer was involved as well.  Just saying. 

Anyways, I stumbled into a nice deep hole which involved things like chunking, iFrames, Silverlight and Flash movies.

With a day of research and writing JavaScript already behind me, I  decided that running face first into the brick wall of the exterior of the office would be more productive than continuing the path I was on.  

So, I started looking for controls. 

And quickly found the Telerik suite of controls. Of which I wanted just the one. Sadly, they don't sell them that way. To use the Telerik upload control, I would have to buy the other dozen or so controls I did not need, want or would ultimately use.  And the DevExpress version was bundled in the exact same way. 

So, I continued my search. 

And found PLUpload (

A quick download, 10 minutes of reading documentation, and then about two hours of coding, I was streaming chunks of data up to my development web server, and into the database. 5 more minutes, I was then generating a link which I could click that would stream those chunks back to me as the image they started life out as. 

My .ASHX had a fairly straightforward logic built into it, and looked like this: 
 Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        context = context
        Request = context.Request
        Response = context.Response
        sessionId = Request.Cookies("uploadSessionId").Value
        If Request.Files.Count > 0 Then
            Dim fileUpload As HttpPostedFile = Request.Files(0)
            Dim fileName As String = fileUpload.FileName
            If String.IsNullOrWhiteSpace(fileName) Or String.Equals(fileName, "blob") Then
                fileName = If(Request("name"), String.Empty)
            End If 'String.IsNullOrWhiteSpace(fileName) Or String.Equals(fileName, "blob")
            Dim tstr As String = If(Request("chunks"), String.Empty)
            Dim chunks As Integer = -1
            If Not Integer.TryParse(tstr, chunks) Then chunks = -1
            tstr = If(Request("chunk"), String.Empty)
            Dim chunk As Integer = -1
            If Not Integer.TryParse(tstr, chunk) Then chunk = -1
            ' If there Then are no chunks sent the file Is sent As one this likely a plain HTML 4 upload (ie. 1 single file)
            If chunks = -1 Then
                fileName = IO.Path.GetFileName(fileName)
                If Not onUploadStart(0, 1, fileName) Then Return
                If onUploadChunk(fileUpload.InputStream, 0, 1, fileName) Then
                End If ' onUploadChunk(fileUpload.InputStream, chunk, chunks, fileName)
            Else ' chunks = -1
                If chunk = 0 Then
                    If Not onUploadStart(chunk, chunks, fileName) Then Return
                End If
                If onUploadChunk(fileUpload.InputStream, chunk, chunks, fileName) Then
                    If chunk >= chunks - 1 Then
                    End If ' chunk >= chunks - 1
                End If ' onUploadChunk(fileUpload.InputStream, chunk, chunks, fileName)
            End If ' chunks = -1
        End If ' Request.Files.Count > 0
    End Sub

Basically, I check for something on the FILES upload, and check how many chunks are supposed to be sent for this file, and which chunk this particular file item is.  The behavior is slightly different for a file that has a single chunk (if chunks = -1), but the basic steps are:
  1. If the first chunk, do upload start (in this case, create a record in the db indicating a file is upload, and the user its associated with)
  2. Save the uploaded chunk somewhere 
  3. if this is the last chunk, tell the db that all chunks are uploaded

Then performing the actual file saving aspects, went something like this:

    Private Function onUploadChunk(fs As IO.Stream, chunk As Integer, numberOfChunks As Integer, fileName As String) As Boolean
        Dim newFileName As String = ""
        Dim stream As IO.Stream
            // set newFileName from DB
            Dim uploadFilePath As String = IO.Path.Combine(Me.fileLocationPath, String.Format("{0}.{1}",newFileName , chunk.ToString("#000")))
            If IO.File.Exists(uploadFilePath) Then IO.File.Delete(uploadFilePath)
            stream = New IO.FileStream(uploadFilePath, IO.FileMode.OpenOrCreate, IO.FileAccess.Write)
            fs.CopyTo(stream, 16384)
        Catch ex As Exception
            ' Need to set status on this file to bad, as  a chunk failed to upload....
            WriteErrorResponse(ex.Message, 100, True)
            Return False
            If stream IsNot Nothing Then stream.Dispose()
        End Try
        Return True
    End Function

Again, fairly straightforward code.  Though a few thinks, my logic records the file that this is, into the database, that means that the file that I'm saving to the file-system, only has a GUID for a name. Then the chunks are given an extension of which chunk they actually are. 

Now, I just need to find the best solution for scanning the files as they're uploaded into the network. 

No comments:

Blog Widget by LinkWithin