c# Фрагментарная передача загрузки файла в сочетании с доступом к параметрам маршрута в ServiceStack

Я хочу использовать интерфейс IRequiresRequestStream, чтобы разрешить загрузку больших файлов (видеофайлов) с использованием ServiceStack (v3) и кодирования передачи по частям. Стандартная загрузка файлов, похоже, не справляется с некоторыми большими видеофайлами, которые загружают наши клиенты, поэтому мы пытаемся включить кодирование передачи по частям для этих файлов.

Я успешно протестировал загрузку закодированного файла с фрагментированной передачей, но есть ряд параметров, которые также необходимо отправить вместе с файлом.

Поскольку IRequiresRequestStream обходит синтаксический анализатор объекта запроса ServiceStack, любые другие параметры в объекте запроса наряду с Stream, очевидно, не заполняются. В качестве обходного пути я вижу следующие варианты:

  1. Параметры строки запроса, доступные через коллекцию this.Request.QueryString
  2. Пользовательские параметры заголовка, доступные через коллекцию this.Request.Headers
  3. Путь, доступный через RequestBinder??

Мне уже удалось реализовать варианты 1 и 2, но почему-то ни один из них не чувствует себя достаточно RESTful. Я бы предпочел использовать Path -> RequestDTO, но у меня проблемы с RequestBinder.

Услуга:

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);
}

Запрос:

[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; }
}

Веб-клиент:

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();
}

Я предполагаю, что простым направлением является что-то вроде следующих строк, но это похоже на кладж.

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;
});

Я немного поискал в Google примеры RequestBinders в этом сценарии. Я уверен, что должны быть встроенные методы ServiceStack для анализа Path, но я борюсь с этим. У кого-нибудь есть пример, которым он хотел бы поделиться?


person Rebecca    schedule 07.06.2017    source источник


Ответы (1)


Недавно я также исследовал использование передачи по частям с пользовательскими заголовками. К сожалению, я обнаружил, что он не поддерживается «из коробки» ни в классе HttpWebRequest, ни в .NET Framework в целом. Единственное решение, которое сработало для меня, заключалось в реализации HTTP-связи Chunked Transfer через TCP. Это не так сложно, как кажется вначале. Вам просто нужно открыть клиентское TCP-соединение, отформатировать заголовки по мере необходимости, разделить поток на куски и отправить его.

Вот определение протокола Chunked Transfer:

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

person Boris Modylevsky    schedule 28.02.2018