c# Sending a file with HttpResponseMessage StreamContent, but no file is sent? - c#

I am trying to send a file on my pc to my client. I have looked around and most answers resembles this: How to return a file (FileContentResult) in ASP.NET WebAPI
So I have tried implementing it myself, but I only manage to receive the headers. I must assume I am doing something wrong somewhere, but I can't tell where. I have a little trouble telling the c# versions apart, but the target it should work on is .Net 4.8.
My code for sending the file:
[HttpGet("ftplog")]
public HttpResponseMessage Get()
{
String fileName = "FileZilla Server.log";
String path = #"C:\Users\jacqq\source\repos\testing\testing\Views\Home\FileZilla Server.log";
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
var memoeryStream = new MemoryStream();
fileStream.CopyTo(memoeryStream);
memoeryStream.Seek(0, SeekOrigin.Begin);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(memoeryStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName.ToString()
};
return result;
}
I have tried sending the FileStream instead of a MemoryStream and I have tried sending the file as ByteArrayContent, but I always only receive headers. I have checked, and the file is read correctly with data. It might be that my recieving code is wrong?
public static async Task DownloadFileTaskAsync(HttpClient client, Uri uri, string FileName)
{
var s = await client.GetStreamAsync(uri);
StreamReader reader = new StreamReader(s) ;
Console.WriteLine("Starting");
Console.WriteLine(reader.ReadToEnd());
Console.WriteLine("stopping");
}
I am new to c#, so it might be I have overlooked something. If anyone know of a different/better way to send a file, I would be keen to hear that too.
Here is what I recieve:
{"version":{"major":1,"minor":1,"build":-1,"revision":-1,"majorRevision":-1,"minorRevision":-1},"content":{"headers":[{"key":"Content-Type","value":["application/octet-stream"]},{"key":"Content-Disposition","value":["attachment; filename=\"FileZilla Server.log\""]}]},"statusCode":100,"reasonPhrase":"Continue","headers":[],"trailingHeaders":[],"requestMessage":null,"isSuccessStatusCode":false}
Please help, have been stuck for little too long.

[HttpGet("ftplog")]
public FileStreamResult Get()
{
string fileName = "FileZilla Server.log";
string path = #"C:\Users\jacqq\source\repos\testing\testing\Views\Home\FileZilla Server.log";
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
var memoryStream = new MemoryStream();
fileStream.SaveAs(memoryStream);
memoryStream.Position = 0;
return new FileStreamResult(memoryStream, "application/octet-stream")
{
FileDownloadName = $"{fileName}.log"
};
}

Related

how to download a css file from a style string in asp.net web api

I want to download a file (css type) from a concatenated style string from the database.I want to return a new file created with the given stylestrings and return on http get request
Your return object is likely using one or both of the IDisposable objects, but disposing of them shortly you return (by way of the using statements).
Manage your memory differently - make calls to stream.Dispose() and httpResponseMessage.Dispose() manually where appropriate - maybe in your class destructor, though in my experience HttpResponseMessage doesn't need disposing as it's dealt with by the garbage collector once you've finished working with it. Example code:
```
byte[] textAsBytes = Encoding.Unicode.GetBytes(concatenatedStyles);
using(MemoryStream stream = new MemoryStream(textAsBytes)) {
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
httpResponseMessage.Content = new StreamContent(stream);
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = "main-theme.scss"
};
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/css");
return ResponseMessage(httpResponseMessage);
}
```
You might need to remove the using around the MemoryStream too, I'm not 100% sure as I can't compile your code on my system.
ResponseMessageResult responseMessageResult;
using (MemoryStream stream = new MemoryStream(textAsBytes))
using (HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK))
{
httpResponseMessage.Content = new StreamContent(stream);
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "main-theme.scss"
};
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/css");
responseMessageResult = ResponseMessage(httpResponseMessage);
}
return responseMessageResult;

ZipArchive Created with System.IO.Compression is Damaged

I am having a hard time creating a ZipArchive successfully on Asp.net core MVC. I have an excel file generated with data that works and I need to put in an archive. This is what I've done so far
public FileResult ExportGoodsReceiptData()
{
var records = _salesService.GetAllReceipts();
var lineRecords = _salesService.GetAllReceiptLines();
var result = _salesService.ExportGoodsReceiptData(records);
var lineResult = _salesService.ExportGoodsReceiptLineData(lineRecords);
byte[] resultArr = StreamToByteArray(result);
byte[] lineResultArr = StreamToByteArray(lineResult);
using(MemoryStream stream = new MemoryStream())
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
var zipArchiveEntry = archive.CreateEntry("GoodsReceipts.csv", CompressionLevel.Fastest);
using (var zipStream = zipArchiveEntry.Open())
using (var resultCom = new MemoryStream(resultArr))
{
resultCom.CopyTo(zipStream);
}
}
return new FileStreamResult(stream, "application/zip") { FileDownloadName = "GoodsReceiptsArchive.zip" };
}
}
When I run it, I get the zipfile, but can't open it. It throws error stating that it may have been damaged. I debugged the code to notice that one of the properties (length property) throws an invalidOperation exception. My approach looks identical to most samples I found online. Don't know how else to solve this. Please help.
Your problem is that you're disposing of your memory stream before you return it. Remove this using:
using(MemoryStream stream = new MemoryStream())
Replace it with:
var stream = new MemoryStream();
Asp.Net MVC will automatically dispose of the stream for you.

Returning a stream from Swagger generated c# server

Swagger has generated a server from an API with a method like this:
[HttpGet]
[Route("SomeRoute")]
[SwaggerOperation("GetFile")]
[SwaggerResponse(200, type: typeof(System.IO.Stream))]
public virtual IActionResult GetFile()
{
string exampleJson = null;
string text = "This could be the contents of a file";
exampleJson = text;
var example = exampleJson != null
? JsonConvert.DeserializeObject<System.IO.Stream>(exampleJson)
: default(System.IO.Stream);
return new ObjectResult(example);
}
If I replace "This could be the contents of a file" with lines of code such as:
var stream = new FileStream(#".\Files\test.txt", FileMode.Open, FileAccess.Read);
var requestBody = new StreamReader(stream,Encoding.ASCII,true).ReadToEnd();
I get a JsonConvert exception saying "invalid characters". What is the correct way to return a stream from swagger generated code?
This seems to work.
var stream = new FileStream(#".\Files\test.txt", FileMode.Open, FileAccess.Read);
var reader = new StreamReader(stream, Encoding.ASCII, true);
return new ObjectResult(reader.BaseStream);

Dispose and return value

I'm confused with streams, return value and dispose. I.e. I use Stream and want to return stream from method. Next code:
public async Task<HttpResponseMessage> GetOverlayAsync(string fileUrl, string language, string strOCR)
{
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
using (var stream = new FileStream(#"D:\\_forTest.jpg", FileMode.Open))
{
length = stream.Length;
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = Path.GetFileName("_forTest.jpg");
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = length;
return result;
}
}
and
public async Task<HttpResponseMessage> GetOverlayAsync(string fileUrl, string language, string strOCR)
{
long length = 0;
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
using (var stream = new FileStream(#"D:\\_forTest.jpg", FileMode.Open))
{
length = stream.Length;
result.Content = new StreamContent(stream);
}
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = Path.GetFileName("_forTest.jpg");
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = length;
return result;
}
returns 504 status code:
ReadResponse() failed: The server did not return a complete response
for this request. Server returned 0 bytes.
so, as I understand, stream is disposed when we go out from method
If I don't call Dispose at all:
public async Task<HttpResponseMessage> GetOverlayAsync(string fileUrl, string language, string strOCR)
{
long length = 0;
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
var stream = new FileStream(#"D:\\_forTest.jpg", FileMode.Open);
length = stream.Length;
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = Path.GetFileName("_forTest.jpg");
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = length;
return result;
}
sometimes I get that file is blocked by another process. How to make it correctly?
Some background:
Generally speaking, if you wish to stream the content of a file, what you want to do is read from the file stream and write to the output stream of HTTP response.
Example:
using (var source = new FileStream(...))
{
byte[] buffer = new byte[4096];
int byteCount;
while ((byteCount = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, byteCount);
}
}
Now, in your particular case, you are using a framework/pattern which requires of you to pass it a stream with your content, instead of allowing you to write to the output yourself. In this scenario, you are forced to shift the responsibility of disposing the stream to the invoker of your handler method.
Specifics:
If your issue is the file being locked, then you can allow shared read/write access when you open the stream:
var stream = new FileStream(#"D:\\_forTest.jpg", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
This will allow other processes to have read and write access to the file while you read from it.
EDIT: As noted by #Evk, a safer option is to only share access with readers (attempts to open the file for writing will be denied):
var stream = new FileStream(#"D:\\_forTest.jpg", FileMode.Open, FileAccess.Read, FileShare.Read);
Improvements:
If your file fits in memory, it would be wise to mem-cache it instead of streaming directly from disk. If you have thousand of simultaneous requests to fetch this file, your disk will become a huge bottleneck. Use a cache with retention/expiration policy and read and cache the entire file on cache misses. This way you also minimize the window in which you have file handle open (open, linear I/O read, close; very fast).

Angular/Web API 2 returns invalid or corrupt file with StreamContent or ByteArrayContent

I'm trying to return a file in a ASP.NET Web API Controller. This file is a dynamically-generated PDF saved in a MemoryStream.
The client (browser) receives the file successfully, but when I open the file, I see that all the pages are totally blank.
The thing is that if I take the same MemoryStream and write it to a file, this disk file is displayed correctly, so I assume that the problem is related to the file transfer via Web.
My controller looks like this:
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
memStream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(memStream.ToArray()); //OR: new StreamContent(memStream);
return result;
}
Just to try, if I write the stream to disk, it's displayed correctly:
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
memStream.Position = 0;
using (var fs = new FileStream("C:\\Temp\\test.pdf", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
memStream.CopyTo(fs);
}
return null;
}
The differences are:
PDF saved on disk: 34KB
PDF transferred via web: 60KB (!)
If I compare both files contents, the main differences are:
File Differences
On the left is the PDF transferred via web; on the right, the PDF saved to disk.
Is there something wrong with my code?
Maybe something related to encodings?
Thanks!
Well, it turned out to be a client (browser) problem, not a server problem. I'm using AngularJS in the frontend, so when the respose was received, Angular automatically converted it to a Javascript string. In that conversion, the binary contents of the file were somehow altered...
Basically it was solved by telling Angular not to convert the response to a string:
$http.get(url, { responseType: 'arraybuffer' })
.then(function(response) {
var dataBlob = new Blob([response.data], { type: 'application/pdf'});
FileSaver.saveAs(dataBlob, 'myFile.pdf');
});
And then saving the response as a file, helped by the Angular File Saver service.
I guess you should set ContentDisposition and ContentType like this:
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(memStream.ToArray())
};
//this line
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "YourName.pdf"
};
//and this line
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
Try this
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
//get buffer
var buffer = memStream.GetBuffer();
//content length for header
var contentLength = buffer.Length;
var statuscode = HttpStatusCode.OK;
var response = Request.CreateResponse(statuscode);
response.Content = new StreamContent(new MemoryStream(buffer));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = contentLength;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=my_filename.pdf", out contentDisposition)) {
response.Content.Headers.ContentDisposition = contentDisposition;
}
return response;
}

Categories