首页 > 解决方案 > Cannot get ReadableStream when using fetch

问题描述

I started developping new web application based on Web API / ReactJS, I have api method with the following signature:

[HttpGet("{clientid}/download/{filename}")]
public async Task<HttpResponseMessage> DownloadFileAsync([FromRoute] int clientid, [FromRoute] string filename)

In the ReactJs side i trying to get a stream from my api by doing the following JS code:

await fetch(`someservice/${clientid}/download/${fileName}`, { responseType: "arraybuffer" })
 .then(function (res) {
     var response = res;
     return response.body;
 })
.then(function (blob) {
    // Create an object URL for the blob object
    const url = URL.createObjectURL(blob);

    console.log(url);

    // Create a new anchor element
    const a = document.createElement('a');

    a.href = url;
    a.download = fileName || 'download';
    a.click();
})

The issue is that i'm recieving a json response like below instead of binary data.

{
   "version":{
      "major":1,
      "minor":1,
      "build":-1,
      "revision":-1,
      "majorRevision":-1,
      "minorRevision":-1
   },
   "content":{
      "headers":[
         {
            "Key":"Content-Type",
            "Value":[
               "application/octet-stream"
            ]
         },
         {
            "Key":"Content-Length",
            "Value":[
               "119503316"
            ]
         }
      ]
   },
   "statusCode":200,
   "reasonPhrase":"OK",
   "headers":[
​
   ],
   "trailingHeaders":[
​
   ],
   "requestMessage":null,
   "isSuccessStatusCode":true
}

Update 1:

I have changed the fetch method like below and now recieve only partial data:

 await fetch(`someservice/${clientid}/download/${fileName}`)
             .then(function (res) {
                 var response = res;
                 return response.body;
             })
            .then(function (body) {
                    var reader = body.getReader()
                    var result;
                    var charsReceived = 0;

                    reader.read().then(function process({ done, value }) {

                        if (done) {
                            console.log("Stream complete");
                            return;
                        }

                        // value for fetch streams is a Uint8Array
                        const chunk = value;
                        charsReceived += value.length;
                        console.log(charsReceived);
                        result += chunk;

                        // Read some more, and call this function again
                        return reader.read().then(process);
                    });

                return result;
            })
            .then(function (blob) {
                // Create an object URL for the blob object
                const url = URL.createObjectURL(blob);

                console.log(url);

                // Create a new anchor element
                const a = document.createElement('a');

                a.href = url;
                a.download = fileName || 'download';
                a.click();
        })

Results

Update 2:

Still have same partial data recieved event after using of arrayBuffer

await fetch(`someservice/${clientid}/download/${fileName}`)
             .then(function (res) {
                 var response = res;
                 return response.arrayBuffer();
             })
            .then(function (blob) {
                console.log(blob);
                // Create an object URL for the blob object
                const url = URL.createObjectURL(blob);

                console.log(url);

                // Create a new anchor element
                const a = document.createElement('a');

                a.href = url;
                a.download = fileName || 'download';
                a.click();
        })

标签: javascriptreactjsblobfetch-apiwebapi

解决方案


I had two issues and I have fixed my javascript like below:

    await fetch(`lynxdumper/${clientid}/download/${fileName}`, { responseType: "blob" })
        .then((response) => {
            return response.blob();
         })
        .then(function (file) {
            console.log(file);
            let blob = new Blob([file], { type: 'dmp' });

            console.log(blob);
            // Create an object URL for the blob object
            const url = URL.createObjectURL(blob);

            console.log(url);

            // Create a new anchor element
            const a = document.createElement('a');

            a.href = url;
            a.download = fileName || 'download';
            a.click();
    })

And I have changed the api side from:

 Task<HttpResponseMessage> to Task<ActionResult<byte[]>> 

The full code of my method is below:

    [HttpGet("{clientid}/download/{filename}")]
    public async Task<IActionResult> DownloadDumpFileAsync([FromRoute] int clientid, [FromRoute] string filename)
    {
        var client = clientRepository.GetClientById(clientid);

        var channel = GrpcChannel.ForAddress(client.BaseUrl.AbsoluteUri, new GrpcChannelOptions
        {
            HttpClient = CreateHttpClient(),
            MaxReceiveMessageSize = 1024 * 1024 * 1024,
            MaxSendMessageSize = 1024 * 1024 * 1024
        });

        var serviceclient = new DumperService.DumperServiceClient(channel);

        var replay = await serviceclient.DownloadAsync(new DowloadRequest { Filename = filename });
        var bytes = replay.Chunk.ToByteArray();

        return Ok(new MemoryStream(bytes));
    }

推荐阅读