Как использовать файл с клиентом ServiceStack - программирование

Как использовать файл с клиентом ServiceStack

Я пытаюсь использовать ServiceStack, чтобы вернуть файл клиенту ServiceStack с помощью метода RESTful.

Я прочитал другие вопросы о SO (здесь и здесь), которые советуют использовать HttpResult и FileInfo объект или MemoryStream, чтобы заголовок ContentType мог быть изменен на соответствующий тип файла.

Это работает для меня, когда я вызываю службу через браузер, правильный файл автоматически загружается. Как я могу использовать файл, используя один из клиентов ServiceStack?

Я использую Request DTO и пытаюсь вернуться с помощью чего-то похожего на

return new HttpResult(new FileInfo("file.xml"), asAttachment:true) {
   ContentType = "text/xml"
};

Как я могу использовать это с помощью JsonServiceClient?

4b9b3361

Ответ 1

Вы не будете использовать файлы с ServiceStack.NET ServiceClients, поскольку они предназначены главным образом для отправки DTO.

Вы можете просто использовать любой обычный WebRequest для загрузки файлов, в v3.9.33 ServiceStack ввели некоторые удобные расширения WebRequest HTTP Utils, которые делают это легко, например:

Для текстового файла:

var xmlFile = downloadUrl.GetXmlFromUrl(responseFilter: httpRes => {
        var fileInfoHeaders = httpRes.Headers[HttpHeaders.ContentDisposition];
    });

Где fileInfoHeaders содержит W3C ContentDisposition HTTP Header, например. при возврате FileInfo, ServiceStack возвращает:

attachment;filename="file.xml";size={bytesLen};
creation-date={date};modification-date={date};read-date={date};

Чтобы загрузить бинарный файл, вы можете использовать:

var rawBytes = downloadUrl.GetBytesFromUrl(httpRes => ...);

Ответ 2

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

на стороне сервера:

службы:

public object Get(FooRequest request)
{
    var stream = ...//some Stream
    return new StreamedResult(stream);
}

Класс StreamedResult:

public class StreamedResult : IHasOptions, IStreamWriter
{
    public IDictionary<string, string> Options { get; private set; }
    Stream _responseStream;

    public StreamedResult(Stream responseStream)
    {
        _responseStream = responseStream;

        long length = -1;
        try { length = _responseStream.Length; }
        catch (NotSupportedException) { }

        Options = new Dictionary<string, string>
        {
            {"Content-Type", "application/octet-stream"},
            { "X-Api-Length", length.ToString() }
        };
    }

    public void WriteTo(Stream responseStream)
    {
        if (_responseStream == null)
            return;

        using (_responseStream)
        {
            _responseStream.WriteTo(responseStream);
            responseStream.Flush();
        }
    }
}

на стороне клиента:

string path = Path.GetTempFileName();//in reality, wrap this in try... so as not to leave hanging tmp files
var response = client.Get<HttpWebResponse>("/foo/bar");

long length;
if (!long.TryParse(response.GetResponseHeader("X-Api-Length"), out length))
    length = -1;

using (var fs = System.IO.File.OpenWrite(path))
    fs.CopyFrom(response.GetResponseStream(), new CopyFromArguments(new ProgressChange((x, y) => { Console.WriteLine(">> {0} {1}".Fmt(x, y)); }), TimeSpan.FromMilliseconds(100), length));

Метод расширения "CopyFrom" был заимствован непосредственно из файла исходного кода "StreamHelper.cs" в этом проекте здесь: Копировать поток с отчетностью о прогрессе (Kudos Хеннинг Дитерихс)

И пригодился мифцу и любому вкладчику ServiceStack. Отличный проект!

Ответ 3

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

Для определения модели следующим образом:

[Route("/filestorage/outgoing/{Name}.{Extension}", "GET")]
[Route("/filestorage/outgoing", "GET")]
public class GetFileStorageStream : IReturn<HttpResult>
{
    public string Name { get; set; }
    public string Extension { get; set; }
    public bool ForDownload { get; set; }
}

Вы можете определить свою службу, чтобы вернуть HttpResult:

public class FileStorageService : Service
{
    public HttpResult Get(GetFileStorageStream fileInformation)
    {
        var internalResult = GetFromFileStorage(fileInformation);
        var fullFilePath = Path.Combine("C:\Temp", internalResult.FileName);
        return new HttpResult(new FileInfo(fullFilePath), asAttachment: fileInformation.ForDownload);
    }
}

Затем на стороне клиента вы можете использовать этот шаблон Get, чтобы правильно получить веб-контекст:

var client = new JsonServiceClient("http://localhost:53842");
var httpResponse = client.Get<HttpWebResponse>("/filestorage/outgoing/test.jpg");
pictureBox1.Image = Image.FromStream(httpResponse.GetResponseStream());

Я обнаружил, что использовать новые методы API Get невозможно, поскольку они попытаются десериализовать HttpResult, который на самом деле не является истинным возвращаемый тип, но класс, представляющий веб-контекст, созданный стекми службы.

Ответ 4

Вы можете перехватить ответ до того, как он будет обработан с помощью фильтра ответа, как показано ниже:

ServiceClientBase.HttpWebResponseFilter = response =>
{
    if (response.Headers["Content-Disposition"] != null)
    {
        var t = response.DownloadText();
        Console.WriteLine(t);
    }
};

Однако это не лучший способ справиться с этим, так как фактический вызов client.Method() приведет к ArgumentException, когда клиент попытается прочитать поток ответов (поскольку он был ранее прочитан response.DownloadFile(...) Я еще не понял способ обработать его изящно, но я обновлю свой ответ, если да.