ASP.Net Zip ActionResult

May 27, 2011 at 11:02 AM

I'm trying to create and return a dotnetzip generated zip as an ActionResult in Asp.net MVC:

 


public ActionResult DownloadZip()
{
	using (var zip = new ZipFile())
	{
		using (var mstream = new MemoryStream())
		{
			using(var writer = new StreamWriter(mstream, Encoding.Default))
			{
				writer.Write(file.ToString());
			}
			zip.AddEntry("someFile.txt", mstream);
		}
		zip.AddDirectory(Server.MapPath("~/NonWeb/someFolder"));
		return new FileStreamResult(zip.GetStream(), "application/octet-stream");
	}
}

However there doesn't appear to be anything like a "GetStream()" method, is there something else I can do to achieve this or does the zip file have to be written to the file system first in order to grab a stream?

May 27, 2011 at 11:36 AM

FWIW I've tried this:

public class ZipResult : ActionResult
    {
        private ZipFile _zipFile;
        private string _filename;

        public ZipResult(ZipFile zipFile, string filename)
        {
            _zipFile = zipFile;
            _filename = filename;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (_zipFile == null) return;

            context.HttpContext.Response.ContentType = "application/octet-stream";
            context.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + _filename);
            // doesn't work
            _zipFile.Save(context.HttpContext.Response.OutputStream);

        }
    }
But the Save method errors with an ObjectDisposedException "Cannot access a closed Stream."

May 27, 2011 at 2:42 PM

Got there in the end with the help of this stackoverflow post, for the benefit of anyone else wanting to do this:

public class ZipResult : ActionResult
    {
        private readonly string[] _filenames;
        private readonly byte[][] _files;
        private readonly string _filename;

        /// <summary>
        /// Creates a streamed zip file for download
        /// </summary>
        /// <param name="filenames">string array of file and foldernames to include in the zip file, folders simply end with a trailing slash ("/" or "\")</param>
        /// <param name="files">byte array of files must be in same order as files in filenames array (folders are skipped)</param>
        /// <param name="filename">default filename for the zip file downloaded</param>
        public ZipResult(string[] filenames, byte[][] files, string filename)
        {
            _filenames = filenames;
            _files = files;
            _filename = filename;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (_files == null) return;

            var response = context.HttpContext.Response;
            response.Clear();
            response.ClearContent();
            response.ClearHeaders();
            response.Cookies.Clear();
            response.ContentType = "application/octet-stream";
            response.ContentEncoding = Encoding.Default;
            response.AddHeader("Content-Type", "application/octet-stream");
            response.BufferOutput = false;
            context.HttpContext.Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}", _filename));

            using (var zipOStream = new ZipOutputStream(context.HttpContext.Response.OutputStream))
            {
                var index = 0;
                foreach(var currentFileName in _filenames)
                {
                    // add filename to stream
                    zipOStream.PutNextEntry(currentFileName);

                    // skip folders
                    if(currentFileName.EndsWith("/") || currentFileName.EndsWith("\\"))
                       continue;

                    // write file contents
                    zipOStream.Write(_files[index], 0, _files[index].Length);
                    index++;
                }
                zipOStream.Flush();
            }
            response.OutputStream.Flush();
        }
    }

Coordinator
May 27, 2011 at 7:50 PM

Glad you worked it out.