简体   繁体   English

c#文件上传分块传输结合ServiceStack中路由参数的访问

[英]c# Chunked transfer of file upload in combination with access to route parameters in ServiceStack

I'm looking to use the IRequiresRequestStream interface to enable large file uploads (video files) using ServiceStack (v3) and chunked transfer encoding.我希望使用IRequiresRequestStream接口来启用使用 ServiceStack (v3) 和分块传输编码的大文件上传(视频文件)。 The standard file upload can't seem to cope with some of the larger video files our customers are uploading, so we are looking to enable chunked transfer encoding for these files.标准文件上传似乎无法处理我们客户上传的一些较大的视频文件,因此我们希望为这些文件启用分块传输编码。

I have successfully tested the chunked transfer encoded file upload, but there are a number of parameters that also need to be sent across with the file.我已经成功测试了分块传输编码文件上传,但是还有许多参数需要与文件一起发送。

Since IRequiresRequestStream bypasses the ServiceStack request object parser, any other parameters in the request object alongside the Stream are obviously not populated.由于IRequiresRequestStream绕过了 ServiceStack 请求对象解析器,因此请求对象中与Stream一起的任何其他参数显然不会被填充。 As a work around I can see the following options:作为解决方法,我可以看到以下选项:

  1. Query String parameters, accessible via this.Request.QueryString collection查询字符串参数,可通过this.Request.QueryString集合访问
  2. Custom header parameters, accessible via this.Request.Headers collection自定义标头参数,可通过this.Request.Headers集合访问
  3. Path, accessible via RequestBinder ??路径,可通过RequestBinder访问?

I've already managed to implement options 1 and 2, but somehow neither feel quite RESTful enough.我已经设法实现了选项 1 和 2,但不知何故都感觉不够 RESTful。 I'd prefer to use the Path -> RequestDTO , but I'm struggling with the RequestBinder .我更喜欢使用Path -> RequestDTO ,但我正在努力使用RequestBinder

Service:服务:

public object Any(AttachmentStreamRequest request)
{
    byte[] fileBytes = null;

    using (var stream = new MemoryStream())
    {
        request.RequestStream.WriteTo(stream);
        length = stream.Length;
        fileBytes = stream.ToArray();
    }

    string filePath = @"D:\temp\test.dat";
    File.WriteAllBytes(filePath, fileBytes);

    var hash = CalculateMd5(filePath);
    var requestHash = this.Request.QueryString["Hash"];
    var customerId = this.Request.QueryString["CustomerId"];
    var fileName = this.Request.QueryString["FileName"];

    // nicer would be
    // var requestHash = request.Hash;
    // var customerId = request.CustomerId;

    // save file....

    // return response
    return requestHash == hash
               ? new HttpResult("File Valid", HttpStatusCode.OK)
               : new HttpResult("Invalid Hash", HttpStatusCode.NotAcceptable);
}

Request:请求:

[Route("/upload/{CustomerId}/{Hash}", "POST", Summary = @"POST Upload attachments for a customer", Notes = "Upload customer attachments")]
public class AttachmentStreamRequest : IRequiresRequestStream
{
    // body
    public Stream RequestStream { get; set; }

    // path    
    public int CustomerId { get; set; }

    // query
    public string FileName { get; set; }

    // query
    public string Comment { get; set; }

    // query
    public Guid? ExternalId { get; set; }

    // path
    public string Hash { get; set; }
}

WebClient:网络客户端:

private static async Task<string> SendUsingWebClient(byte[] file, string hash, customerId)
{
    var client = (HttpWebRequest)WebRequest.Create(string.Format("http://localhost.fiddler:58224/upload/{0}/{1}", customerId, hash));
    client.Method = WebRequestMethods.Http.Post;
    client.Headers.Add("Cookie", "ss-pid=XXXXXXXXXXX; ss-id=YYYYYYYYYY");

    // the following 4 rows enable streaming 
    client.AllowWriteStreamBuffering = false;
    client.SendChunked = true;
    client.ContentType = "application/json";
    client.Timeout = int.MaxValue;

    using (var fileStream = new MemoryStream(file))
    {
        fileStream.Copy(client.GetRequestStream());
    }

    return new StreamReader(client.GetResponse().GetResponseStream()).ReadToEnd();
}

I'm guessing the simple direction to take is something along the following lines, but it seems like a kludge.我猜想采取的简单方向是以下几行,但这似乎是一团糟。

RequestBinders.Add(typeof(AttachmentStreamRequest), httpReq => { 
    var dto = new AttachmentStreamRequest(); 
    var segments = base.Request.PathInfo.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

    dto.CustomerId = segments[1].As<int32>();
    dto.Hash = segments[2].As<string>();

    // Stream copy to dto.RequestStream and other params etc....

    return dto;
});

I've done a bit of Googling for examples of RequestBinders in this scenario.在这种情况下,我已经对RequestBinders示例进行了一些谷歌搜索。 I'm sure there must be inbuilt ServiceStack methods for parsing the Path , but I'm struggling with it.我确信必须有内置的 ServiceStack 方法来解析Path ,但我正在努力解决它。 Does anyone have an example they would like to share?有没有人想分享一个例子?

Recently I also investigated using Chunked transfer with custom headers.最近我还研究了使用带有自定义标头的分块传输。 Unfortunately, I found out that it's not supported out-of-the-box in HttpWebRequest class nor in .NET Framework in general.不幸的是,我发现它在 HttpWebRequest 类和 .NET Framework 中都不支持开箱即用。 The only solution that worked for me was to implement Chunked Transfer HTTP communication over TCP.唯一对我有用的解决方案是通过 TCP 实现分块传输 HTTP 通信。 It's not as complex as it sounds in the begginning.它并不像开始时听起来那么复杂。 You just need to open TCP client connection, format the headers as needed, split your stream by chunks and send it.您只需要打开 TCP 客户端连接,根据需要格式化标头,按块拆分流并发送。

Here is the definition of Chunked Transfer protocol:以下是分块传输协议的定义:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM