skip to Main Content

I want to write export/download functionality for files from external API.
I’ve created separate Action for it. Using external API I can get stream for that file.
When I am saving that stream to local file, everything is fine, file isn’t empty.

var exportedFile = await this.GetExportedFile(client, this.ReportId, this.WorkspaceId, export);

// Now you have the exported file stream ready to be used according to your specific needs
// For example, saving the file can be done as follows:
string pathOnDisk = @"D:Temp" + export.ReportName + exportedFile.FileSuffix;
                
using (var fileStream = File.Create(pathOnDisk))
{
    await exportedFile.FileStream.CopyToAsync(fileStream);
}

But when I return exportedFile object that contains in it stream and do next:

var result = await this._service.ExportReport(reportName, format, CancellationToken.None);

var fileResult = new HttpResponseMessage(HttpStatusCode.OK);
                
using (var ms = new MemoryStream())
{
    await result.FileStream.CopyToAsync(ms);
    ms.Position = 0;

    fileResult.Content = new ByteArrayContent(ms.GetBuffer());
}
            
fileResult.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                                                {
                                                    FileName = $"{reportName}{result.FileSuffix}"
                                                 };

fileResult.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

return fileResult;

Exported file is always empty.

Is it problem with stream or with code that try to return that stream as file?


Tried as @Nobody suggest to use ToArray

fileResult.Content = new ByteArrayContent(ms.ToArray());

the same result.


Also tried to use StreamContent

fileResult.Content = new StreamContent(result.FileStream);

still empty file.

But when I’m using StreamContent and MemmoryStream

using (var ms = new MemoryStream())
{
     await result.FileStream.CopyToAsync(ms);
     ms.Position = 0;

     fileResult.Content = new StreamContent(ms);
}

in result I got

{
"error": "no response from server"
}


Note: from 3rd party API I get stream that is readonly.

2

Answers


  1. you used GetBuffer() to retrieve the data of the memory stream.
    The function you should use is ToArray()
    Please read the Remarks of the documentation of these functions.

    https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream.getbuffer?view=net-6.0

    using (var ms = new MemoryStream())
    {
        ms.Position = 0;
        await result.FileStream.CopyToAsync(ms);
    
        fileResult.Content = new ByteArrayContent(ms.ToArray()); //ToArray() and not GetBuffer()
    }
        
    
    Login or Signup to reply.
  2. Your "mistake" although it’s an obvious one is that you return a status message, but not the actual file itself (which is in it’s own also a 200).

    You return this:

    var fileResult = new HttpResponseMessage(HttpStatusCode.OK);
    

    So you’re not sending a file, but a response message. What I’m missing in your code samples is the procedure call itself, but since you use a HttpResonseMessage I will assume it’s rather like a normal Controller action. If that is the case you could respond in a different manner:

    return new FileContentResult(byteArray, mimeType){ FileDownloadName = filename };
    

    where byteArray is ofcourse just a byte[], the mimetype could be application/octet-stream (but I suggest you’d actually find the correct mimetype for the browser to act accordingly) and the filename is the filename you want the file to be named.

    So, if you were to stitch above and my comment together you’d get this:

    var exportedFile = await this.GetExportedFile(client, this.ReportId, this.WorkspaceId, export);
    
    // Now you have the exported file stream ready to be used according to your specific needs
    // For example, saving the file can be done as follows:
    string pathOnDisk = @"D:Temp" + export.ReportName + exportedFile.FileSuffix;
                    
    using (var fileStream = File.Create(pathOnDisk))
    {
        await exportedFile.FileStream.CopyToAsync(fileStream);
    }
    
    return new FileContentResult(System.IO.File.ReadAllBytes(pathOnDisk), "application/octet-stream") { FileDownloadName = export.ReportName + exportedFile.FileSuffix };
    

    I suggest to try it, since you still report a 200 (and not a fileresult)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search