guizmo
guizmo

Reputation: 105

Get the file postion of each file into a stream

I am sending multiples files from my web api but I want to read each part of the stream to convert him into a byte array , then at the end I have a list of byte[], and I can save each files:

 [Route("GetFiles")]
    public HttpResponseMessage GetFile([FromUri] List<string> filesNames)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);

        if (filesNames.Count == 0)
            return Request.CreateResponse(HttpStatusCode.BadRequest);

        var content = new MultipartContent();

        filesNames.ForEach(delegate (string fileName)
        {
            string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads/" + fileName);
            byte[] pdf = File.ReadAllBytes(filePath);

            content.Add(new ByteArrayContent(pdf));
            response.Headers.Add(fileName, fileName);
        });

        var files = JsonConvert.SerializeObject(content);

        response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
        response = Request.CreateResponse(HttpStatusCode.OK, content);

        return response;
    }

Here is how I get one file into a stream, then convert him into a byte array to report the process percentage :

public static async Task<byte[]> CreateDownloadTaskForFile(string urlToDownload, IProgress<DownloadBytesProgress> progessReporter)
    {
        int receivedBytes = 0;
        int totalBytes = 0;
        WebClient client = new WebClient();

        using (var stream = await client.OpenReadTaskAsync(urlToDownload))
        {

            byte[] buffer = new byte[BufferSize];
            totalBytes = Int32.Parse(client.ResponseHeaders[HttpResponseHeader.ContentLength]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                for (; ; )
                {
                    int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    memoryStream.Write(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        await Task.Yield();
                        break;
                    }

                    receivedBytes += bytesRead;
                    if (progessReporter != null)
                    {
                        DownloadBytesProgress args = new DownloadBytesProgress(urlToDownload, receivedBytes, totalBytes);
                        progessReporter.Report(args);
                    }
                }
                return memoryStream.ToArray();
            }
        }
    }

How do I get the position of a stream for each files send ?

Update :

I made a HttpResponseMessage like this :

[Route("GetFiles")]
    public HttpResponseMessage GetFiles([FromUri] List<string> filesNames)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);

        if (filesNames.Count == 0)
            return Request.CreateResponse(HttpStatusCode.BadRequest);

        var content = new MultipartFormDataContent();

        filesNames.ForEach(delegate (string fileName)
        {
            string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads/" + fileName);
            byte[] pdf = File.ReadAllBytes(filePath);

            content.Add(new ByteArrayContent(pdf), fileName);
        });

        response = Request.CreateResponse(HttpStatusCode.OK, content);
        response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

        return response;
    }

But from my device side : When I am trying to run the request But there is nothing on the response content :

using (var httpResponseMessage = await httpClient.GetAsync(urlToDownload + filesNamesArg))
            {
                var streamProvider = new MultipartMemoryStreamProvider();

                streamProvider = httpResponseMessage.Content.ReadAsMultipartAsync().Result;
            }

Could you show me some docs or advice ?

Upvotes: 0

Views: 278

Answers (1)

Oguz Ozgul
Oguz Ozgul

Reputation: 7187

What?

This answer provides a 100% working example for:

  1. Serving multiple files as a single response from a web API using multipart/mixed content type,
  2. Reading the file contents on the client by parsing the response of the web API implemented in 1

I hope this helps.


Server:

The server application is a .Net 4.7.2 MVC project with web API support.

The following method is implemented in an ApiController and returns all the files under the ~/Uploads folder in a single response.

Please make note of the use of Request.RegisterForDispose extension to register the FileStreams for later disposal.

    public async Task<HttpResponseMessage> GetFiles()
    {
        string filesPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads");

        List<string> fileNames = new List<string>(Directory.GetFiles(filesPath));

        var content = new MultipartContent();

        fileNames.ForEach(delegate(string fileName)
        {
            var fileContent = new StreamContent(File.OpenRead(fileName));
            Request.RegisterForDispose(fileContent);
            fileContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("image/jpeg");
            content.Add(fileContent);
        });

        var response = new HttpResponseMessage();
        response.Content = content;
        return response;
    }

The response's Content-Type header shows as Content-Type: multipart/mixed; boundary="7aeff3b4-2e97-41b2-b06f-29a8c23a7aa7" and each file is packed in different blocks separated by the boundary.

Client:

The client application is a .Net Core 3.0.1 console application.

Please note the synchronous usage of the async methods. This can be easily changed to asynchronous using await, but implemented like this for simplicity:

using System;
using System.IO;
using System.Net.Http;

namespace console
{
    class Program
    {
        static void Main(string[] args)
        {
            using (HttpClient httpClient = new HttpClient())
            {
                using (HttpResponseMessage httpResponseMessage = httpClient.GetAsync("http://localhost:60604/api/GetImage/GetFiles").Result)
                {
                    var content = (HttpContent)new StreamContent(httpResponseMessage.Content.ReadAsStreamAsync().Result);
                    content.Headers.ContentType = httpResponseMessage.Content.Headers.ContentType;
                    MultipartMemoryStreamProvider multipartResponse = new MultipartMemoryStreamProvider();
                    content.ReadAsMultipartAsync(multipartResponse);
                    for(int i = 0; i< multipartResponse.Contents.Count;i++)
                    {
                        Stream contentStream = multipartResponse.Contents[i].ReadAsStreamAsync().Result;
                        Console.WriteLine("Content {0}, length {1}", i, contentStream.Length);
                    }
                }
            }
        }
    }
}

Upvotes: 1

Related Questions