C# save a file from a HTTP Response - c#

Im trying to download and save a file from a HttpWebResponse but im having problems saving the file (other than Text Files) properly.
I think its something to do with this part:
byte[] byteArray = Encoding.UTF8.GetBytes(http.Response.Content);
MemoryStream stream = new MemoryStream(byteArray);
Text Files work fine with the above code but when I try to save the Content to an Image file it gets corrupted.
How do i write this 'string' data to an image file (and other binary files)
Forgot to mention, This is .NET CP 3.5 and I have a wrapper class around the HttpWebResponse class to add OAuth etc.

The problem is you're interpreting the binary data as text, even if it isn't - as soon as you start treating the content as a string instead of bytes, you're in trouble. You haven't given the details of your wrapper class, but I'm assuming your Content property is returning a string - you won't be able to use that. If your wrapper class doesn't let you get at the raw data from the web response, you'll need to modify it.
If you're using .NET 4, you can use the new CopyTo method:
using (Stream output = File.OpenWrite("file.dat"))
using (Stream input = http.Response.GetResponseStream())
{
input.CopyTo(output);
}
If you're not using .NET 4, you have to do the copying manually:
using (Stream output = File.OpenWrite("file.dat"))
using (Stream input = http.Response.GetResponseStream())
{
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}

Use WebClient.DownloadFile. You can do it manually (something like this), but WebClient is the best bet for simple downloads.

Simplest way I found is:
Use ReadAsByteArrayAsync to get the data.
List item
File.WriteAllBytes to save the file.
No need to loop through anything. Example in context below:
[TestClass]
public class HttpTests
{
[TestMethod]
public void TestMethod1()
{
var jsObject = (dynamic)new JObject();
jsObject.html = "samplehtml.html";
jsObject.format = "jpeg";
const string url = "https://mywebservice.com/api";
var result = GetResponse(url, jsObject).GetAwaiter().GetResult();
File.WriteAllBytes($#"c:\temp\httpresult.{jsObject.format}", result);
}
static async Task<byte[]> GetResponse(string uri, dynamic jsObj)
{
var httpClient = new HttpClient();
var load = jsObj.ToString();
var content = new StringContent(load, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(uri, content);
return await response.Content.ReadAsByteArrayAsync();
}
}

Related

Can save stream as local file, but when returning it as HttpResponseMessage - always empty file

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.
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()
}
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)

How to save the docx binary data to file in web api C#

I'm fetching a byte[] of a docx file from one API to another and processing it like Application.Documents.Open(array); or File.WriteAllBytes(path, array);
I think the data received is in some UTF-8 format but i have no idea how to convert and process them
Fetching the file from the API using below code.
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
return result;
}
The output(byte[]) is received as string like this.
string result = "PK[][]?......";
Please check the postman screenshot
And trying with below code to save to folder but not working
byte[] res = result .ToArray();
File.WriteAllBytes(#"C:\temp\myfile.docx", res);
also tried this but didn't work,
byte[] mybytearray = Convert.FromBase64String(t);
byte[] barr = Encoding.ASCII.GetBytes(hardcode);
Maybe you should use BinaryReader instead of StreamReader.
See also: https://stackoverflow.com/a/8613300/1438829

VirusTotal Uploaded File is Zero Bytes

I'm trying to upload a file to VirusTotal using .Net Core.But the uploaded file size is Zero Bytes.Why does this happen?
[Route("api/[controller]")]
public class ScannerController : Controller
{ [HttpGet]
public async Task<VirusTotalNet.Results.FileReport> ScanAsync(string file_id)
{
file_id = "./wwwroot/Upload/node-v12.14.1-x64.msi";
VirusTotal virusTotal = new VirusTotal("");
// virusTotal.UseTLS = true;
FileStream stream = System.IO.File.OpenRead(file_id);
byte[] fileBytes = new byte[stream.Length];
stream.Read(fileBytes, 0, fileBytes.Length);
VirusTotalNet.Results.FileReport report = await virusTotal.GetFileReportAsync(stream);
return report;
}
}
You've read the entire file into a byte[] and there's an overload of GetFileReportAsync that will take that, so change the parameter from stream to fileBytes:
VirusTotalNet.Results.FileReport report = await virusTotal.GetFileReportAsync(fileBytes);
Derviş Kayımbaşıoğlu suggested resetting the stream's position but it turns out that the location mentioned was incorrect. Either of these:
stream.Seek(0L, SeekOrigin.Begin);
// or
stream.Position = 0L;
Needed to be done immediately before calling GetFileReportAsync, after the file had been read, not before. That would've worked.
But wait, there's more!
There's no need to read the file into fileBytes, which means there's no need to reset the position. The stream can be opened and passed directly to GetFileReportAsync. Including proper resource disposal, the entire method becomes this:
[HttpGet]
public async Task<VirusTotalNet.Results.FileReport> ScanAsync(string file_id)
{
file_id = "./wwwroot/Upload/node-v12.14.1-x64.msi";
VirusTotal virusTotal = new VirusTotal("");
// virusTotal.UseTLS = true;
using (FileStream stream = System.IO.File.OpenRead(file_id))
{
VirusTotalNet.Results.FileReport report = await virusTotal.GetFileReportAsync(stream);
return report;
}
}
This allows both the file to be read and the socket to be written asynchronously, and the data can be buffered in small amounts so that large files don't have to be loaded entirely into memory.

How to return a FileResult using an Amazon S3 ResponseStream?

Using the AWSSDK.S3 nuget package, I'm trying to return a file that has been retrieved from an S3 bucket. My starting point is based on the example given in this SO answer.
Example controller code:
public FileResult GetFile(Guid id)
{
// no using block as it will be disposed of by the File method
var amazonResponse = _foo.GetAmazonResponseWrapper(_user, id);
// set Response content-length header
// set Response content-type header
var bufferSize = 1024;
var buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = amazonResponse.ResponseStream.Read(buffer, 0, buffer.Length)) > 0 && Response.IsClientConnected)
{
Response.OutputStream.Write(buffer, 0, bytesRead);
Response.OutputStream.Flush();
buffer = new byte[bufferSize];
}
// this will not work (can't read from this stream)
return File(Response.OutputStream, "text/plain", "bar.txt");
}
If I write to a MemoryStream I create and use in the while loop, I will get a file, but there won't be any content.
The only way I've found to get content in the file is to call .ToArray() on the stream like so:
return File(memStream.ToArray(), "text/plain", "foo.txt");
Is there a way to actually stream a file to the browser without loading it into the memory of the web server?
Just pass the stream forward
public FileResult GetFile(Guid id) {
// no using block as it will be disposed of by the File method
var amazonResponse = _foo.GetAmazonResponseWrapper(_user, id);
return File(amazonResponse.ResponseStream, "text/plain", "bar.txt");
}
You have already shown that you can read from the response stream. Then just pass the response stream on and the File result will read it and return the response.

How to get the stream for a Multipart file in webapi upload?

I need to upload a file using Stream (Azure Blobstorage), and just cannot find out how to get the stream from the object itself. See code below.
I'm new to the WebAPI and have used some examples. I'm getting the files and filedata, but it's not correct type for my methods to upload it. Therefore, I need to get or convert it into a normal Stream, which seems a bit hard at the moment :)
I know I need to use ReadAsStreamAsync().Result in some way, but it crashes in the foreach loop since I'm getting two provider.Contents (first one seems right, second one does not).
[System.Web.Http.HttpPost]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var provider = GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
// On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
// so this is how you can get the original file name
var originalFileName = GetDeserializedFileName(result.FileData.First());
// uploadedFileInfo object will give you some additional stuff like file length,
// creation time, directory name, a few filesystem methods etc..
var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
// Remove this line as well as GetFormData method if you're not
// sending any form data with your upload request
var fileUploadObj = GetFormData<UploadDataModel>(result);
Stream filestream = null;
using (Stream stream = new MemoryStream())
{
foreach (HttpContent content in provider.Contents)
{
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, content.ReadAsStreamAsync().Result);
stream.Position = 0;
filestream = stream;
}
}
var storage = new StorageServices();
storage.UploadBlob(filestream, originalFileName);**strong text**
private MultipartFormDataStreamProvider GetMultipartProvider()
{
var uploadFolder = "~/App_Data/Tmp/FileUploads"; // you could put this to web.config
var root = HttpContext.Current.Server.MapPath(uploadFolder);
Directory.CreateDirectory(root);
return new MultipartFormDataStreamProvider(root);
}
This is identical to a dilemma I had a few months ago (capturing the upload stream before the MultipartStreamProvider took over and auto-magically saved the stream to a file). The recommendation was to inherit that class and override the methods ... but that didn't work in my case. :( (I wanted the functionality of both the MultipartFileStreamProvider and MultipartFormDataStreamProvider rolled into one MultipartStreamProvider, without the autosave part).
This might help; here's one written by one of the Web API developers, and this from the same developer.
Hi just wanted to post my answer so if anybody encounters the same issue they can find a solution here itself.
here
MultipartMemoryStreamProvider stream = await this.Request.Content.ReadAsMultipartAsync();
foreach (var st in stream.Contents)
{
var fileBytes = await st.ReadAsByteArrayAsync();
string base64 = Convert.ToBase64String(fileBytes);
var contentHeader = st.Headers;
string filename = contentHeader.ContentDisposition.FileName.Replace("\"", "");
string filetype = contentHeader.ContentType.MediaType;
}
I used MultipartMemoryStreamProvider and got all the details like filename and filetype from the header of content.
Hope this helps someone.

Categories