I am developing some REST API with C# and Net Core
I have a function in my repository which accepts a parameter of type IFormFile.
public async Task<bool> UploadFile(IFormFile file)
{
// do some stuff and save the file to azure storage
}
This function is called by a controller method which pass it the uploaded file
public class FileController : Controller
{
public async Task<IActionResult> UploadDoc(IFormFile file
{
// Call the repository function to save the file on azure
var res = await documentRepository.UploadFile(file);
}
}
Now I have another function that calls an external API which returns a file as a byte array. I'd like to save this byte array using the repository.UploadFile method but I can't cast the byte array object to IFormFile.
Is it possible?
You can convert the byte array to a MemoryStream:
var stream = new MemoryStream(byteArray);
..and then pass that to the constructor of the FromFile class:
IFormFile file = new FormFile(stream, 0, byteArray.Length, "name", "fileName");
Your repo shouldn't be using IFormFile. That's an abstraction that only applies to one particular method of HTTP file transfer (namely a multipart/form-data encoded request body). Something like your repo should have no knowledge of the source of the file (HTTP), nor how it was transmitted (multipart/form-data vs application/json for example).
Instead, you should use Stream for your param. In your UploadDoc action, then, you can simply do:
using (var stream = file.OpenReadStream())
{
await documentRepository.UploadFile(stream);
}
And, where you have just a byte array:
using (var stream = new MemoryStream(byteArray))
{
await documentRepository.UploadFile(stream);
}
You might also consider adding an overload of UploadFile that takes a byte[], as creating a new memory stream from a byte array just to have a stream is a waste of resources. However, a byte[] has to be handled differently than a Stream, so it may require some duplication of logic to go that route. You'll need to evaluate the tradeoffs.
Create a new MemoryStream based on the byte array.
Create a new FormFile object based on the MemoryStream.
Make sure to append the ContentDisposition header, otherwise you will be unable to operate your FormFile object as a C# exception will be thrown.
The complete code:
using (var stream = new MemoryStream(byteArray))
{
var file = new FormFile(stream, 0, byteArray.Length, name, fileName)
{
Headers = new HeaderDictionary(),
ContentType = contentType,
};
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
{
FileName = file.FileName
};
file.ContentDisposition = cd.ToString();
}
Related
I call an API to get a PDF file. The API returns it as a string with binary data.
Now I need to save it to a file without any conversion of the data.
How can I do this in C#?
I have been trying
string file = await service.GetDocumentsAsync(document.FileId); // Gets the filedata
byte[] byteArray = file.Select (c => (byte)c).ToArray ();
using (var stream = new FileStream($"c:\\temp\\{document.Id}.pdf", FileMode.Create))
{
stream.Write (byteArray,0,file.Length);
stream.Close ();
}
I do get the PDF, but it only has blank pages.
The beginning of the string when i look at it in the Debugger:
As suggested we had to change what the API returned. We use RestSharp and had to use Response.RawByte iso. Response.
I am trying to return a PDF file from my ASP.NET Core 2 controller.
I have this code
(mostly borrowed from this SO question):
var net = new System.Net.WebClient();
//a random pdf file link
var fileLocation = "https://syntera.io/documents/T&C.pdf";/
var data = net.DownloadData(fileLocation);
MemoryStream content = null;
try
{
content = new MemoryStream(data);
return new FileStreamResult(content, "Application/octet-stream");
}
finally
{
content?.Dispose();
}
This code above is part of a service class that my controller calls. This is the code from my controller.
public async Task<IActionResult> DownloadFile(string fileName)
{
var result = await _downloader.DownloadFileAsync(fileName);
return result;
}
But I keep getting ObjectDisposedException: Cannot access a closed Stream.
The try and finally block was an attempt to fix it , from another SO question .
The main question is A) Is this the right way to send a PDF file back to the browser and B) if it isn't, how can I change the code to send the pdf to the browser?
Ideally , I don't want to first save the file on the server and then return it to the controller. I'd rather return it while keeping everything in memory.
The finally will always get called (even after the return) so it will always dispose of the content stream before it can be sent to the client, hence the error.
Ideally , I don't want to first save the file on the server and then return it to the controller. I'd rather return it while keeping everything in memory.
Use a FileContentResult class to take the raw byte array data and return it directly.
FileContentResult: Represents an ActionResult that when executed will write a binary file to the response.
async Task<IActionResult> DownloadFileAsync(string fileName){
using(var net = new System.Net.WebClient()) {
byte[] data = await net.DownloadDataTaskAsync(fileName);
return new FileContentResult(data, "application/pdf") {
FileDownloadName = "file_name_here.pdf"
};
}
}
No need for the additional memory stream
You must specify :
Response.AppendHeader("content-disposition", "inline; filename=file.pdf");
return new FileStreamResult(stream, "application/pdf")
For the file to be opened directly in the browser.
I'm building a simple API using .NET core, and would like to send a simple .json file to the client once he reaches a certain endpoint on the API.
So far I'm having very little success, but what I currently have is the following:
public IActionResult yaddayadda(){
var filePath = "./Data/file.json";
using (var stream = new FileStream(#filePath, FileMode.Open))
{
return new FileStreamResult(stream, "application/json");
}
}
This gets me nothing. (The path for the file is correct)
Thanks!
EDIT: I've experimented with different content-types, and even though it's not the correct one, multipart/form-data allows me to download a file, but it has no extension.
Try this, by avoiding the disposal (so the closure) of the stream:
public IActionResult yaddayadda(){
var filePath = "./Data/file.json";
var stream = new FileStream(#filePath, FileMode.Open);
return new FileStreamResult(stream, "application/json");
}
UPDATE: Here is another way I use with pictures, although should behave the same:
public IActionResult yaddayadda()
{
var filePath = "./Data/file.json";
return this.PhysicalFile(filePath, "application/json");
}
Note that this solution implies that you're deriving from the Controller class, because the "PhysicalFile" method is exposed by.
Try to use
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
string fileName = "file.json";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Useful link.
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.
I have a method in Service1.svc.cs, below is the code
public void SaveData(int UserId, System.IO.MemoryStream File)
{
//Some code
}
I am passing values from xaml.cs
savedata.SaveDataAsync(userId, ms);
The error is
cannot convert from 'System.IO.MemoryStream' to
'SignSilverlight.ServiceReference1.MemoryStream'
How to solve ?
Memory stream is a .NET local object and it is not possible to pass it to a remote machine that might not even run .NET.
You have to pass a byte[] array instead. But be aware of size limits in endpoint's settings.
Here is how to (de)serialize a memory stream to array
// first endpoint
var streamSending = new MemoryStream();
var array = streamSending.ToArray();
// second endpoint
var streamRecieving = new MemoryStream(array);