ZIP-ed HTTP upload

Nov 30, 2009 at 9:37 AM
Edited Nov 30, 2009 at 9:39 AM

Below is code using ICSharpCode.SharpZipLib.Zip to ZIP the data before upload.

 

HttpFileUploader.cs

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using mpost.SilverlightFramework;
using System.Windows.Threading;
/*
 * Copyright Michiel Post
 * http://www.michielpost.nl
 * contact@michielpost.nl
 * */
namespace mpost.SilverlightMultiFileUpload.Classes
{

    public class HttpFileUploader : IFileUploader
    {
        #region Business-Logic, not Upload related
        string aid = System.Windows.Browser.HtmlPage.Document.QueryString["aid"];
        string cid = System.Windows.Browser.HtmlPage.Document.QueryString["cid"];
        string uid = System.Windows.Browser.HtmlPage.Document.QueryString["uid"];
        string sid = System.Windows.Browser.HtmlPage.Document.QueryString["sid"];
        #endregion
        private UserFile _file;
        private long _dataLength;
        private long _dataSent;
        private string _initParams;
        private long ChunkSize = 4194304;
        private string UploadUrl;
        // Stream object pointer for both ZIP or uncompressed upload. Used for writing to Web Request
        System.IO.Stream _file_FileStream;
#if ZIP
        private long _EstimatedFileBytesSent; // Estimated Number of Byte of the file that was sent
        // If Zip, the progress bar and progress byte / percentage counter need to be adjusted also
        // Siliverlight backend zip compression
        ICSharpCode.SharpZipLib.Zip.ZipOutputStream ZOS;
#endif
        public HttpFileUploader(UserFile file, string httpHandlerName)
        {
            _file = file;

            _dataLength = _file.FileStream.Length;
            _dataSent = 0;

            if(string.IsNullOrEmpty(httpHandlerName))
                httpHandlerName = "HttpUploadHandler.ashx";

            UploadUrl = new CustomUri(httpHandlerName).ToString();
        }
               
        #region IFileUploader Members


        /// <summary>
        /// Start the file upload
        /// </summary>
        /// <param name="initParams"></param>
        public void StartUpload(string initParams)
        {
            _initParams = initParams;

            StartUpload();
        }        

        /// <summary>
        /// Cancel the file upload
        /// </summary>
        public void CancelUpload()
        {
            //Not implemented yet...
        }

        public event EventHandler UploadFinished;

        #endregion

        private void StartUpload()
        {
            long dataToSend = _dataLength - _dataSent;
            bool isLastChunk = dataToSend <= ChunkSize;
            bool isFirstChunk = _dataSent == 0;

            UriBuilder httpHandlerUrlBuilder = new UriBuilder(UploadUrl);
            #region Added extra parameter
            httpHandlerUrlBuilder.Query = string.Format("{5}file={0}&offset={1}&last={2}&first={3}&param={4}&aid={6}&cid={7}&uid={8}&sid={9}", _file.FileName, _dataSent, isLastChunk, isFirstChunk, _initParams, string.IsNullOrEmpty(httpHandlerUrlBuilder.Query) ? "" : httpHandlerUrlBuilder.Query.Remove(0, 1) + "&",aid,cid,uid,sid);
            #endregion
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(httpHandlerUrlBuilder.Uri);
            webRequest.Method = "POST";
            webRequest.BeginGetRequestStream(new AsyncCallback(WriteToStreamCallback), webRequest);

            
        }

        private void WriteToStreamCallback(IAsyncResult asynchronousResult)
        {
            HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
            Stream requestStream = webRequest.EndGetRequestStream(asynchronousResult);

            byte[] buffer = new Byte[4096];
            int bytesRead = 0;
            int tempTotal = 0;

            //Set the start position

            // Read File to Byte array in memory
#if ZIP
            #region ZIP file.FileStream to ZipOutoutStream and then convert to a MemoryStream
            // ZIP the N-th chunk each time
            _file_FileStream = new System.IO.MemoryStream();
            int BufferSize = (int)(_file.FileSize - _EstimatedFileBytesSent >= ChunkSize ? ChunkSize : _file.FileSize - _EstimatedFileBytesSent);
            byte[] FileDataBuffer = new byte[BufferSize];
            // Copy a chuck of data to Buffer
            // Always read from buffer head. Position = ZERO
            _file.FileStream.Read(FileDataBuffer, (int)0, BufferSize);
            // ZIP the data to a memory stream (_file_FileStream)
            ZOS = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(_file_FileStream);
            // Write Byte to ZipOutputStream (Zip the data)
            ICSharpCode.SharpZipLib.Zip.ZipEntry ZE = new ICSharpCode.SharpZipLib.Zip.ZipEntry(_file.FileName);
            ZOS.PutNextEntry(ZE);
            ZOS.Write(FileDataBuffer, 0, FileDataBuffer.Length);
            ZOS.CloseEntry();
            ZOS.Flush();

            //_file_FileStream will now contained zipped data of the N-th chuck.
            //Position always 0 because the ZOS contains only data in one single chuck of original file
            _file_FileStream.Position = 0;
            #endregion
#else
                _file_FileStream = _file.FileStream;
                //Set the start position
                _file_FileStream.Position = _dataSent;
#endif

            //_file_FileStream.Close();

            //Read the next chunk
            while ((bytesRead = _file_FileStream.Read(buffer, 0, buffer.Length)) != 0 && tempTotal + bytesRead < ChunkSize && !_file.IsDeleted && _file.State != Constants.FileStates.Error)
            {
                requestStream.Write(buffer, 0, bytesRead);
                requestStream.Flush();
#if ZIP
                // ZIP data sent / total zip size * Buffer size=Esimated data sent, for progress display
                _dataSent += (long)(bytesRead * (double)FileDataBuffer.Length / (double)ZOS.Length);
                //adjustment needed since _dataSent was an estimate only. Don't want to display "> 100%"
                if (_dataSent >= _file.FileSize)
                    _dataSent = (long)_file.FileSize;
#else
                _dataSent += bytesRead;
#endif
                tempTotal += bytesRead;

                //Notify progress change
                _file.UIDispatcher.BeginInvoke(delegate()
                {
                    OnProgressChanged();
                });
            }
#if ZIP
            // Estimated File Byte sent in each chuck = buffer size
            _EstimatedFileBytesSent += BufferSize;

            // last chuck, adjustment needed since _dataSent was an estimate only.
            if (BufferSize < ChunkSize)
                _dataSent = (long)_file.FileSize;
#endif
            //Leave the fileStream OPEN
            //fileStream.Close();

            requestStream.Close();

            //Get the response from the HttpHandler
            webRequest.BeginGetResponse(new AsyncCallback(ReadHttpResponseCallback), webRequest);

        }

        private void ReadHttpResponseCallback(IAsyncResult asynchronousResult)
        {
            bool error = false;

            //Check if the response is OK
            try
            {
                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
                HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
                StreamReader reader = new StreamReader(webResponse.GetResponseStream());

                string responsestring = reader.ReadToEnd();
                reader.Close();
            }
            catch ( Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString() + ":"+ex.ToString());
                error = true;

                _file.UIDispatcher.BeginInvoke(delegate()
               {
                   _file.State = Constants.FileStates.Error;
               });
            }

            if (_dataSent < _dataLength)
            {
                //Not finished yet, continue uploading
                if (_file.State != Constants.FileStates.Error && !error)
                    StartUpload();
            }
            else
            {
                _file_FileStream.Close();
                _file_FileStream.Dispose();

                //Finished event
                _file.UIDispatcher.BeginInvoke(delegate()
                {
                    if (UploadFinished != null)
                        UploadFinished(this, null);
                });
            }

        }

        private void OnProgressChanged()
        {
            _file.BytesUploaded = _dataSent;
        }

        #region Write Log to browser debug/error console
        public static void Browser_Console_Log(object message)
        {
            System.Windows.Browser.HtmlPage.Window.Eval("console.log");
            var log = (System.Windows.Browser.HtmlPage.Window.Eval("console.log") as System.Windows.Browser.ScriptObject);
            if (log != null)
            {
                log.InvokeSelf(message);

            }
        }
        #endregion

    }
}

 

--------------------Modify HttpUploadHandler.ashx

#if !ORIGINAL

            // ORIGINAL code, save context.Request.InputStream directly
            using (FileStream fs = File.Open(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName, FileMode.Append))
            {
                SaveFile(context.Request.InputStream, fs);
                fs.Close();
            }
#else
            // Generic file stream pointer for both compressed file stream
            Stream UploadedFileStream;
            ICSharpCode.SharpZipLib.Zip.ZipInputStream ZIS = new ICSharpCode.SharpZipLib.Zip.ZipInputStream(context.Request.InputStream);
            ICSharpCode.SharpZipLib.Zip.ZipEntry ZE = ZIS.GetNextEntry();
            UploadedFileStream = ZIS; // Read from ZipInputStream will return uncompressed data
            int FileStreamLength = (int)UploadedFileStream.Length;

            // You can then write the uploadedfilestream data to server file system
            //_FileManager.SaveFileStream(UploadedFileStream, tempFilePath);
            
#endif

 

Coordinator
Nov 30, 2009 at 5:59 PM

Did you test the upload speed before and after this implemenation? How much faster is it?

Thanks!
Michiel

Dec 1, 2009 at 2:48 AM

Sorry, I didn’t test the speed.

I think the ICSharp Zip library have the average ZIP compression ratio.