我创建了一个自定义操作结果,用于发送从其他服务器下载的多个文件的压缩存档。
很抱歉有很多代码,但确实存在。
using ICSharpCode.SharpZipLib.Zip;
/// <summary>
/// Downloads the files using specified URLs and continuously streams zipped result to the client
/// </summary>
public class MultipleFileZipResult : ActionResult
{
private readonly ILog log;
private const int BufferSize = 32 * 1024;
/// <summary>
/// Initializes a new instance of the <see cref="MultipleFileZipResult"/> class.
/// </summary>
/// <param name="urls">The URLs to download.</param>
/// <param name="resultFileName">Name of the result file.</param>
/// <param name="log">The logger.</param>
public MultipleFileZipResult(IEnumerable<DownloadableZipDescriptor> urls, string resultFileName, ILog log)
{
this.log = log;
this.Urls = urls;
this.ResultFileName = resultFileName;
}
/// <summary>
/// Gets or sets the list of URLs to download.
/// </summary>
public IEnumerable<DownloadableZipDescriptor> Urls { get; set; }
/// <summary>
/// Gets or sets the name of the zip result file.
/// </summary>
public string ResultFileName { get; set; }
/// <summary>
/// Enables processing of the result of an action method by a custom type that inherits from the <see cref="T:System.Web.Mvc.ActionResult"/> class.
/// </summary>
/// <param name="context">The context in which the result is executed. The context information includes the controller, HTTP content, request context, and route data.</param>
public override void ExecuteResult(ControllerContext context)
{
try
{
context.HttpContext.Response.ContentType = "application/zip";
context.HttpContext.Response.CacheControl = "private";
context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.HttpContext.Response.AddHeader("content-disposition", string.Format("attachment; filename=\"{0}\"", this.ResultFileName));
context.HttpContext.Response.Flush();
var buffer = new byte[BufferSize];
using (var zippedUploadStream = new ZipOutputStream(context.HttpContext.Response.OutputStream))
{
zippedUploadStream.SetLevel(0);
foreach (var url in this.Urls)
{
Stream downloadStream = null;
WebResponse response = null;
try
{
var request = WebRequest.Create(url.DownloadUrl);
request.Proxy = null;
response = request.GetResponse();
downloadStream = response.GetResponseStream();
if (downloadStream != null)
{
var zipEntry = new ZipEntry(url.SaveFileName);
zippedUploadStream.PutNextEntry(zipEntry);
int read;
while ((read = downloadStream.Read(buffer, 0, buffer.Length)) > 0)
{
zippedUploadStream.Write(buffer, 0, read);
context.HttpContext.Response.Flush();
}
}
}
catch (Exception exception)
{
this.log.Error(exception);
}
finally
{
if (response != null)
{
response.Close();
}
if (downloadStream != null)
{
downloadStream.Close();
}
}
if (!context.HttpContext.Response.IsClientConnected)
{
break;
}
}
zippedUploadStream.Finish();
}
if (context.HttpContext.Response.IsClientConnected)
{
context.HttpContext.Response.Flush();
context.HttpContext.Response.End();
}
}
catch (Exception exception)
{
this.log.Error(exception);
throw;
}
}
}
一切正常,直到客户试图从服务器所在的同一网络下载压缩档案。奇怪的是,由于某种原因,下载对我来说很好。
唯一明显的区别是我和他的下载速度。他的下载速度约为11 mbps,而我的下载速度为0.4 mbps。
记录的异常:
System.Web.HttpException (0x800704CD): The remote host closed the connection. The error code is 0x800704CD.
at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)
at System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush()
at System.Web.HttpResponse.Flush(Boolean finalFlush)
at System.Web.HttpResponseWrapper.Flush()
System.Web.HttpException (0x80004005): An error occurred while communicating with the remote host. The error code is 0x800703E3. ---> System.Runtime.InteropServices.COMException (0x800703E3): The I/O operation has been aborted because of either a thread exit or an application request. (Exception from HRESULT: 0x800703E3)
at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)
at System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush()
at System.Web.HttpResponse.Flush(Boolean finalFlush)
at System.Web.HttpResponseWrapper.Flush()
at Infrastructure.MultipleFileZipResult.ExecuteResult(ControllerContext context)