Posting MultiPartFileData file to a function - c#

I have data coming from a source to my Api and I need to post it to another POST ActionResult.
I have no problem in receiving data in first function which is basically this;
var provider = new MultipartFormDataStreamProvider(root);
try
{
//irrelevant confirmations and other code pieces are left out
await Request.Content.ReadAsMultipartAsync(provider);
foreach (MultipartFileData fileData in provider.FileData)
{
var appPath = System.AppDomain.CurrentDomain.BaseDirectory.ToString();
var basePath = Path.GetFullPath(Path.Combine(appPath, #"..\"));
var headerActivityPath = basePath + "\\Documents\\" + tenantString + "\\Activity\\" + activityId;
File.Copy(fileData.LocalFileName, Path.Combine(activityPath, fileName));
}
}
I need to make an POST after this one finishes receiving the file. I want to make it without saving it to disk first, so I don't know where I should do the POST request with HttpClient.
The ActionResult with will be receiving the data has a parameter with HttpPostedFileBase but I don't know what to send it.
Every method I used before uses a file on the disk, is it possible to do this without saving the file to disk first?

HttpPostedFileBase has a property on it called InputStream this will give you access to the content of the attachment which was uploaded to your endpoint. You can use this as the argument to a new StreamContent in the HttpClient.
foreach (MultipartFileData fileData in provider.FileData)
{
await client.PostAsync("http://someurl.com", new StreamContent(fileData.InputStream));
}

Related

Returning a PDF from an ASP.NET Core 2 Controller

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.

This method or property is not supported after HttpRequest.GetBufferlessInputStream has been invoked

I am trying to browse and upload a file from client to server using Angular Js and WEB API.I used Input file type for user to select file and post the file to WEB API. In web API, I am getting following error "This method or property is not supported after HttpRequest.GetBufferlessInputStream has been invoked."
I am using the following code:-
public IHttpActionResult UploadForm()
{
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = System.Web.HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = System.Web.HttpContext.Current.Server.MapPath("~/UploadFile/" + postedFile.FileName);
postedFile.SaveAs(filePath);
}
}
return Json("Document Saved");
}
I get this error when i tried to get files from HTTP request... should I update anything in web config??
Please help me to resolve this issue..
try this it work fine for me.
//get the root folder where file will be store
string root = HttpContext.Current.Server.MapPath("~/UploadFile");
// Read the form data.
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.FileData.Count > 0 && provider.FileData[0] != null)
{
MultipartFileData file = provider.FileData[0];
//clean the file name
var fileWithoutQuote = file.Headers.ContentDisposition.FileName.Substring(1, file.Headers.ContentDisposition.FileName.Length - 2);
//get current file directory on the server
var directory = Path.GetDirectoryName(file.LocalFileName);
if (directory != null)
{
//generate new random file name (not mandatory)
var randomFileName = Path.Combine(directory, Path.GetRandomFileName());
var fileExtension = Path.GetExtension(fileWithoutQuote);
var newfilename = Path.ChangeExtension(randomFileName, fileExtension);
//Move file to rename existing upload file name with new random filr name
File.Move(file.LocalFileName, newfilename);
}
}
I also had the same problem. And the solution by #Jean did not work for me.
I need to upload some CSV file and had to use it in the controller.
In Javascript, I used Fetch API to upload the csv file.
But, in the controller, I used this code:
[HttpPost]
[CatchException]
public bool ImportBundlesFromCsv()
{
var a = Request.Content.ReadAsByteArrayAsync();
//convert to Stream if needed
Stream stream = new MemoryStream(a.Result); // a.Result is byte[]
// convert to String if needed
string result = System.Text.Encoding.UTF8.GetString(a.Result);
// your code
return true;
}
This worked for me. Hope this helps!

Read Multipart/Form-Data without touching disk

Alright, so I want to receive a Multipart/Form-Data POST request in my WebAPI, and do things with the files and form fields contained within. That's easy, like so:
var provider = new MultipartFormDataStreamProvider(Path.GetTempPath());
await Request.Content.ReadAsMultipartAsync(provider);
var formValue = provider.FormData["form_value_here"];
var files = provider.FileData.Select(d => d.LocalFileName);
It's easy to get any form field or particular file I need. However, I would like to have similar capability, but instead of saving files to disk when ReadAsMultipartAsync is called, I'd like to have them in a Stream
The reasoning for this, is that I would like to hash the values of the file(s) being posted, and reject the request if necessary, before saving the files to disk. Is there built in provider or class that I've missed that has a convenient API?
You can use MultipartMemoryStreamProvider to get the form data as a stream. You'll want something like this:
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var fileNames = new List<string>();
foreach (var file in provider.Contents)
{
fileNames.Add(file.Headers.ContentDisposition.FileName.Trim('\"'));
var buffer = await file.ReadAsByteArrayAsync();
//hash values, reject request if needed
}
return Ok();

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.

How to Upload two different format files to two different file paths using MultipartFormDataStreamProvider in Asp.Net Web API

My client app will be sending me an image file and a video file through Multi part form data stream. And i want to store the image file in image folder and the video file in the video folder.
This is how the request from the client looks like
Content-Disposition: form-data; name="Photo"; filename="IMG_9322.JPG"
Content-Type:application/octet-stream
Content-Disposition: form-data; name="Video"; filename="sample.avi"
Content-Type:application/octet-stream
And this is the Api controller method at my end
[HttpPost]
[ActionName("UploadVideoEntry")]
public async Task<HttpResponseMessage> UploadVideoEntry()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var folderName = "photos";
string root = HttpContext.Current.Server.MapPath("~/" + folderName);
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data and return an async task.
var response = await Request.Content.ReadAsMultipartAsync(provider);
return Request.CreateResponse(HttpStatusCode.OK, new ResponseMessage<Object> { success = true, message = "Media Uploaded" });
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
This stores both the files in the photo folder because thats the path i have given. But i dont know how i can separate both the files and store them in different paths. Please let me know if anyone has any idea? Thanks
Look at the examples on the following page:
http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2
You want to get the file names after the files are downloaded and move them. I have done something very similar. Once you have the filename you can use
File.Move(currentPath,newPath)
to move the file to the new location. Your currentPath is your root variable plus the filename. Don't forget to add the #"\" as such:
currentPath = root + #"\" + filename
Below is code for getting the file name.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
My own first step would be to test the filename to determine the extension and then perform whatever action you want, in this case a move, once you find the appropriate file.

Categories