so I made an uploader in my web API by using multipart form data but the problem is when I save my pictures from the file stream it also gave me the content here
-----------------------------7e1e364095c
Content-Disposition: form-data; name="file"; filename="C:\Users\kewin\Downloads\windows 10 pro.jpg"
Content-Type: image/jpeg
the binary starts from here
and if i remove thoose 4 lines i can watch my picture so are there any way to remove that so I am only left with the picture
public async Task<IHttpActionResult> UploadImage(string fileName = "")
{
if (fileName == "")
{
fileName = Guid.NewGuid().ToString();
}
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
return BadRequest("Could not find file to upload");
}
var provider = await Request.Content.ReadAsMultipartAsync(new InMemoryMultipartFormDataStreamProvider());
var files = provider.Files;
var uploadedFile = files[0];
var extension = ExtractExtension(uploadedFile);
var contentType = uploadedFile.Headers.ContentType.ToString();
var savePath = ConfigurationManager.AppSettings["savePath"];
var file = string.Concat(savePath, fileName, extension);
try
{
var request = HttpContext.Current.Request;
var fileDir = file + request.Headers[""];
using (var fs = new FileStream(fileDir, FileMode.Create))
{
request.InputStream.CopyTo(fs);
}
return Ok();
}
catch (StorageException e)
{
return BadRequest(e.Message);
}
try
{
var fileInfo = new UploadedFileInfo
{
FileName = fileName,
FileExtension = extension,
ContentType = contentType,
FilePath = savePath + imageFile
};
return Ok(fileInfo);
}
Currently you read picture data from request.InputStream, which contains whole unparsed multipart content, including headers you don't need. Instead, you should read picture data from InMemoryMultipartFormDataStreamProvider you created, which parses input stream and provides you simple access to headers and data stream of individual uploaded file(s).
....
var provider = await Request.Content.ReadAsMultipartAsync(new InMemoryMultipartFormDataStreamProvider());
var files = provider.Files;
var uploadedFile = files[0];
var extension = ExtractExtension(uploadedFile);
var contentType = uploadedFile.Headers.ContentType.ToString();
var savePath = ConfigurationManager.AppSettings["savePath"];
var file = string.Concat(savePath, fileName, extension);
try
{
using (var fs = new FileStream(file, FileMode.Create))
{
await uploadedFile.CopyToAsync(fs);
}
return Ok();
}
....
Related
I am trying to download Excel file using web API but I am unable to download file in postman where as I am able to download Excel file when I enter URL in browser though while opening file I get warning message like below :
When i hit endpoint using POSTMAN then file get corrupted and it is showing junk characters.
Code :
protected virtual byte[] ExportToXlsx<T>(IEnumerable<T> itemsToExport)
{
using (var stream = new MemoryStream())
{
using (var xlPackage = new ExcelPackage())
{
// get handles to the worksheets
var worksheet = xlPackage.Workbook.Worksheets.Add(typeof(T).Name);
//create Headers and format them
var manager = new PropertyManager<T>(itemsToExport.First());
manager.WriteCaption(worksheet, SetCaptionStyle);
var row = 2;
foreach (var items in itemsToExport)
{
manager.CurrentObject = items;
manager.WriteToXlsx(worksheet, row++, false);
}
xlPackage.Save();
}
return stream.ToArray();
}
}
private readonly IServiceContext ctx;
public void Download(string guid)
{
var bytes = ExportToXlsx(list);
ctx.reqobj.HttpContext.Response.Headers.Add("Content-Disposition", "attachment; filename=\"demo.xlsx\"");
ctx.reqobj.HttpContext.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
ctx.reqobj.HttpContext.Response.Body.Write(bytes, 0, bytes.Length);
}
Note : I am using OfficeOpenXml for Excel file creation.
I will appreciate any help.
Update :
Try using "Send and download" instead of "Send"
https://www.getpostman.com/docs/v6/postman/sending_api_requests/responses
Postman doesn't download any file just return you the data that the server or your service provides. i have a project that download an excel to with the OpenXML here is an example with which you can guide with some styles to.
[HttpGet]
public void DownloadTable(int id)
{
List<Employee> all = db.Employees.Where(x => x.ManagerId == id).ToList();
String file = "Example.xlsx";
String path = Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data"), file);
List<string[]> headerRow = new List<string[]>() { new string[] { "EmployeeId", "Name", "Shift", "Timestamp" } };
string headerRange = "A2:" + Char.ConvertFromUtf32(headerRow[0].Length + 64) + "2";
ExcelPackage excel = new ExcelPackage();
excel.Workbook.Worksheets.Add("Employees");
var page = excel.Workbook.Worksheets["Employees"];
page.Cells["A1:D1"].Merge = true;
page.Cells["A1:D1"].Value = "Supervisor: " + all.FirstOrDefault().Manager + " - " + id;
page.Cells["A1:D1"].Style.Font.Bold = true;
page.Cells[headerRange].LoadFromArrays(headerRow);
int z = 3;
foreach (Reporte r in all)
{
page.Cells["A" + z].Value = r.Id;
page.Cells["B" + z].Value = r.Name;
page.Cells["C" + z].Value = r.Shift;
page.Cells["D" + z].Value = r.Timestamp;
z++;
}
page.Cells["D3:D" + z].Style.Numberformat.Format = "dddd dd MMMM YYYY";
page.Cells["A2:D2"].AutoFilter = true;
page.Cells["A1:D" + z].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
page.Cells["A1:D" + z].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
page.Cells["A2:D" + z].AutoFitColumns();
page.Cells["A1:D1"].Style.Fill.PatternType = ExcelFillStyle.Solid;
page.Cells["A1:D1"].Style.Fill.BackgroundColor.SetColor(Color.FromArgb(1, 183, 222, 232));
FileInfo excelFile = new FileInfo(path);
excel.SaveAs(excelFile);
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/plain";
response.AddHeader("Content-Disposition",
"attachment; filename=" + file + ";");
response.TransmitFile(path);
response.Flush();
response.End();
File.Delete(path);
}
The stream needs to be passed to the package.
Right now nothing is being given to the package,
//...
using (var xlPackage = new ExcelPackage())
//...
So nothing is being saved to the stream, which is why the error is shown when trying to open the file.
There is no need to convert the memory stream to an array. Return the stream and pass that along for the response.
protected virtual Stream ExportToXlsx<T>(IEnumerable<T> itemsToExport) {
var stream = new MemoryStream();
using (var xlPackage = new ExcelPackage(stream)) { //<<< pass stream
// get handles to the worksheets
var worksheet = xlPackage.Workbook.Worksheets.Add(typeof(T).Name);
//create Headers and format them
var manager = new PropertyManager<T>(itemsToExport.First());
manager.WriteCaption(worksheet, SetCaptionStyle);
var row = 2;
foreach (var items in itemsToExport) {
manager.CurrentObject = items;
manager.WriteToXlsx(worksheet, row++, false);
}
xlPackage.Save();
}
return stream;
}
A controller action to return the file would look like this
public IActionResult Download(string guid) {
//...get list
var file = ExportToXlsx(list);
var contentType = "application/vnd.openxmlformats";
var fileName = "demo.xlsx";
return File(file, contentType, fileName); //returns a FileStreamResult
}
It was indicated in comments that the above is done in a support method.
Using the same approach
private readonly IServiceContext ctx;
//...
public void Download(string guid) {
//...get list
using(var fileStream = ExportToXlsx(list)) {
if (fileStream.CanSeek && fileStream.Position != 0) {
fileStream.Seek(0, SeekOrigin.Begin);
}
var contentType = "application/vnd.openxmlformats";
var fileName = "demo.xlsx";
var response = ctx.reqobj.HttpContext.Response;
response.Headers.Add("Content-Disposition", $"attachment; filename=\"{fileName}\"");
response.Headers.Add("Content-Length", fileStream.Length.ToString());
response.ContentType = contentType;
fileStream.CopyTo(response.Body);
}
}
the generated file is copied over to the body of the response.
As for postman, the tool is simply showing the content return in the response. It does not try to download the actual file as an attachment.
I have a web service that I can call and save the returned csv file. Everything seems to be working OK. What I am now interested in doing is returning multiple CSV files for the user to download. What is the proper way to handle this? I'm guessing I need a way to package them up (zip? perhaps)?
[HttpPost]
[Route("OutputTemplate")]
public HttpResponseMessage OutputTemplate()
{
HttpResponseMessage msg = new HttpResponseMessage();
string body = this.Request.Content.ReadAsStringAsync().Result;
try
{
string contents = DoStuff(body) // get contents based on body
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(contents);
writer.Flush();
stream.Position = 0;
msg.StatusCode = HttpStatusCode.OK;
msg.Content = new StreamContent(stream);
msg.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
msg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "fileexport"
};
return msg;
}
...
}
Using the following model to abstract file name and content
public class FileModel {
public string FileName { get; set; }
public byte[] FileContent { get; set; }
}
The following extension was derived to compress the file content
public static class ZipArchiveExtensions {
public static Stream Compress(this IEnumerable<FileModel> files) {
if (files.Any()) {
var ms = new MemoryStream();
using(var archive = new ZipArchive(
stream: ms,
mode: ZipArchiveMode.Create,
leaveOpen: true
)){
foreach (var file in files) {
var entry = archive.add(file);
}
}
ms.Position = 0;
return ms;
}
return null;
}
private static ZipArchiveEntry add(this ZipArchive archive, FileModel file) {
var entry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest);
using (var stream = entry.Open()) {
stream.Write(file.FileContent, 0, file.FileContent.Length);
}
return entry;
}
}
With that in place, the example API controller action could look something like this.
public class ExampleApiController : ApiController {
public async Task<IHttpActionResult> OutputTemplate() {
IHttpActionResult result = BadRequest();
var body = await Request.Content.ReadAsStreamAsync();
List<FileModel> files = DoSomething(body);
if (files.Count > 1) {
//compress the files.
var archiveStream = files.Compress();
var content = new StreamContent(archiveStream);
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = "fileexport.zip"
};
result = ResponseMessage(response);
} else if (files.Count == 1) {
//return the single file
var fileName = files[0].FileName; //"fileexport.csv"
var content = new ByteArrayContent(files[0].FileContent);
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = fileName
};
result = ResponseMessage(response);
}
return result;
}
private List<FileModel> DoSomething(System.IO.Stream body) {
//...TODO: implement file models
throw new NotImplementedException();
}
}
I have created an endpoint that takes an arbitrary file:
[HttpPost()]
public async Task<IActionResult> CreateFile(IFormFile file)
When I test it with Postman, the file is always null.
Here is what I am doing in Postman:
What am I doing wrong?
Thanks to #rmjoia's comment I got it working! Here is what I had to do in Postman:
The complete solution for uploading file or files is shown below:
This action use for uploading multiple files:
// Of course this action exist in microsoft docs and you can read it.
HttpPost("UploadMultipleFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
// Full path to file in temp location
var filePath = Path.GetTempFileName();
foreach (var formFile in files)
{
if (formFile.Length > 0)
using (var stream = new FileStream(filePath, FileMode.Create))
await formFile.CopyToAsync(stream);
}
// Process uploaded files
return Ok(new { count = files.Count, path = filePath});
}
The postman picture shows how you can send files to this endpoint for uploading multiple files:
This action use for uploading single file:
[HttpPost("UploadSingleFile")]
public async Task<IActionResult> Post(IFormFile file)
{
// Full path to file in temp location
var filePath = Path.GetTempFileName();
if (file.Length > 0)
using (var stream = new FileStream(filePath, FileMode.Create))
await file.CopyToAsync(stream);
// Process uploaded files
return Ok(new { count = 1, path = filePath});
}
The postman picture shows how you can send a file to this endpoint for uploading single file:
Your should be like that
[HttpPost]
public async Task<IActionResult> UploadFile([FromForm]UploadFile updateTenantRequest)
{
}
Your class should be like:-
public class UpdateTenantRequestdto
{
public IFormFile TenantLogo { get; set; }
}
and then
[HttpPost("UploadSingleFile"), Route("[action]")]
public async Task<IActionResult> UploadSingleFile([FromForm(Name = "file")] IFormFile file)
{
// Process uploaded files
string folderName = "Uploads";
string webRootPath = hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
Repository.Models.File fileModel = new Repository.Models.File();
fileModel.Name = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
fileModel.Path = $"{folderName}/{file.FileName}";
fileModel.Size = file.Length;
fileModel.Type = file.ContentType;
string fullPath = Path.Combine(newPath, fileModel.Name);
fileModel.Extension = Path.GetExtension(fullPath);
fileModel.CreatedDate = Utility.Common.GetDate;
fileModel.CreatedBy = 1;
//fileModel save to db
using (var stream = new FileStream(fullPath, FileMode.Create))
{
//file.CopyTo(stream);
await file.CopyToAsync(stream);
}
return Ok(new { count = 1, path = filePath });
}
I have a return File that is offering to download a file but is also downloading the file to another location, I would just like it to offer to download to the user one file, i.e. it reads the initial data from memory, so the first argument in the return File is a MemoryStream of some sort, but I can't figure out how to do it
[HttpPost]
public FilePathResult FileToFasta(F2FModel model)
{
string FullText = new StreamReader(model.File.InputStream).ReadToEnd();
TextLayer layer = new TextLayer(FullText);
string outputFile = layer.WriteToFasta();
String mydatetime = DateTime.Now.ToString("MMddyyyy");
string FileName = String.Format("TextFile{0}.txt", mydatetime);
string FilePath = #"F:\test\" + FileName;
FileInfo info = new FileInfo(FilePath);
if (!info.Exists)
{
using (StreamWriter writer = info.CreateText())
{
writer.Write(outputFile);
}
}
return File(FilePath, "text/plain", FileName);
}
Thanks
MemoryStream can be used with FileStreamResult, for example like this:
[HttpPost]
public FilePathResult FileToFasta(F2FModel model)
{
string FullText = new StreamReader(model.File.InputStream).ReadToEnd();
TextLayer layer = new TextLayer(FullText);
string outputFile = layer.WriteToFasta();
string mydatetime = DateTime.Now.ToString("MMddyyyy");
string FileName = String.Format("TextFile{0}.txt", mydatetime);
//Use different encoding if needed
byte[] outputArray = Encoding.Unicode.GetBytes(outputFile);
MemoryStream outputStream = new MemoryStream(outputArray);
//FileStreamResult will close the stream for you so don't worry
return new FileStreamResult(outputStream, "text/plain") { FileDownloadName = FileName };
}
I'm using asp.net mvc 4 webapi beta to build a rest service. I need to be able to accept POSTed images/files from client applications. Is this possible using the webapi? Below is how action I am currently using. Does anyone know of an example how this should work?
[HttpPost]
public string ProfileImagePost(HttpPostedFile profileImage)
{
string[] extensions = { ".jpg", ".jpeg", ".gif", ".bmp", ".png" };
if (!extensions.Any(x => x.Equals(Path.GetExtension(profileImage.FileName.ToLower()), StringComparison.OrdinalIgnoreCase)))
{
throw new HttpResponseException("Invalid file type.", HttpStatusCode.BadRequest);
}
// Other code goes here
return "/path/to/image.png";
}
I'm surprised that a lot of you seem to want to save files on the server. Solution to keep everything in memory is as follows:
[HttpPost("api/upload")]
public async Task<IHttpActionResult> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
//Do whatever you want with filename and its binary data.
}
return Ok();
}
see http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime#multipartmime, although I think the article makes it seem a bit more complicated than it really is.
Basically,
public Task<HttpResponseMessage> PostFile()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
string file1 = provider.BodyPartFileNames.First().Value;
// this is the file name on the server where the file was saved
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
See the code below, adapted from this article, which demonstrates the simplest example code I could find. It includes both file and memory (faster) uploads.
public HttpResponseMessage Post()
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count < 1)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
foreach(string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
postedFile.SaveAs(filePath);
// NOTE: To store in memory use postedFile.InputStream
}
return Request.CreateResponse(HttpStatusCode.Created);
}
The ASP.NET Core way is now here:
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
// full path to file in temp location
var filePath = Path.GetTempFileName();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
// process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size, filePath});
}
Here is a quick and dirty solution which takes uploaded file contents from the HTTP body and writes it to a file. I included a "bare bones" HTML/JS snippet for the file upload.
Web API Method:
[Route("api/myfileupload")]
[HttpPost]
public string MyFileUpload()
{
var request = HttpContext.Current.Request;
var filePath = "C:\\temp\\" + request.Headers["filename"];
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
{
request.InputStream.CopyTo(fs);
}
return "uploaded";
}
HTML File Upload:
<form>
<input type="file" id="myfile"/>
<input type="button" onclick="uploadFile();" value="Upload" />
</form>
<script type="text/javascript">
function uploadFile() {
var xhr = new XMLHttpRequest();
var file = document.getElementById('myfile').files[0];
xhr.open("POST", "api/myfileupload");
xhr.setRequestHeader("filename", file.name);
xhr.send(file);
}
</script>
I used Mike Wasson's answer before I updated all the NuGets in my webapi mvc4 project. Once I did, I had to re-write the file upload action:
public Task<HttpResponseMessage> Upload(int id)
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType));
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);
string guid = Guid.NewGuid().ToString();
File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
Apparently BodyPartFileNames is no longer available within the MultipartFormDataStreamProvider.
Toward this same directions, I'm posting a client and server snipets that send Excel Files using WebApi, c# 4:
public static void SetFile(String serviceUrl, byte[] fileArray, String fileName)
{
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var content = new MultipartFormDataContent())
{
var fileContent = new ByteArrayContent(fileArray);//(System.IO.File.ReadAllBytes(fileName));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
content.Add(fileContent);
var result = client.PostAsync(serviceUrl, content).Result;
}
}
}
catch (Exception e)
{
//Log the exception
}
}
And the server webapi controller:
public Task<IEnumerable<string>> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
MyMultipartFormDataStreamProvider streamProvider = new MyMultipartFormDataStreamProvider(fullPath);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var fileInfo = streamProvider.FileData.Select(i =>
{
var info = new FileInfo(i.LocalFileName);
return "File uploaded as " + info.FullName + " (" + info.Length + ")";
});
return fileInfo;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
}
}
And the Custom MyMultipartFormDataStreamProvider, needed to customize the Filename:
PS: I took this code from another post http://www.codeguru.com/csharp/.net/uploading-files-asynchronously-using-asp.net-web-api.htm
public class MyMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public MyMultipartFormDataStreamProvider(string path)
: base(path)
{
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
string fileName;
if (!string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName))
{
fileName = headers.ContentDisposition.FileName;
}
else
{
fileName = Guid.NewGuid().ToString() + ".data";
}
return fileName.Replace("\"", string.Empty);
}
}
[HttpPost]
public JsonResult PostImage(HttpPostedFileBase file)
{
try
{
if (file != null && file.ContentLength > 0 && file.ContentLength<=10485760)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/") + "HisloImages" + "\\", fileName);
file.SaveAs(path);
#region MyRegion
////save imag in Db
//using (MemoryStream ms = new MemoryStream())
//{
// file.InputStream.CopyTo(ms);
// byte[] array = ms.GetBuffer();
//}
#endregion
return Json(JsonResponseFactory.SuccessResponse("Status:0 ,Message: OK"), JsonRequestBehavior.AllowGet);
}
else
{
return Json(JsonResponseFactory.ErrorResponse("Status:1 , Message: Upload Again and File Size Should be Less Than 10MB"), JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
return Json(JsonResponseFactory.ErrorResponse(ex.Message), JsonRequestBehavior.AllowGet);
}
}
This question has lots of good answers even for .Net Core. I was using both Frameworks the provided code samples work fine. So I won't repeat it. In my case the important thing was how to use File upload actions with Swagger like this:
Here is my recap:
ASP .Net WebAPI 2
To upload file use: MultipartFormDataStreamProvider see answers here
How to use it with Swagger
.NET Core
To upload file use: IFormFile see answers here or MS documentation
How to use it with Swagger
Here are two ways to accept a file. One using in memory provider MultipartMemoryStreamProvider and one using MultipartFormDataStreamProvider which saves to a disk. Note, this is only for one file upload at a time. You can certainty extend this to save multiple-files. The second approach can support large files. I've tested files over 200MB and it works fine. Using in memory approach does not require you to save to disk, but will throw out of memory exception if you exceed a certain limit.
private async Task<Stream> ReadStream()
{
Stream stream = null;
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var buffer = await file.ReadAsByteArrayAsync();
stream = new MemoryStream(buffer);
}
return stream;
}
private async Task<Stream> ReadLargeStream()
{
Stream stream = null;
string root = Path.GetTempPath();
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.FileData)
{
var path = file.LocalFileName;
byte[] content = File.ReadAllBytes(path);
File.Delete(path);
stream = new MemoryStream(content);
}
return stream;
}
I had a similar problem for the preview Web API. Did not port that part to the new MVC 4 Web API yet, but maybe this helps:
REST file upload with HttpRequestMessage or Stream?
Please let me know, can sit down tomorrow and try to implement it again.
API Controller :
[HttpPost]
public HttpResponseMessage Post()
{
var httpRequest = System.Web.HttpContext.Current.Request;
if (System.Web.HttpContext.Current.Request.Files.Count < 1)
{
//TODO
}
else
{
try
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
BinaryReader binReader = new BinaryReader(postedFile.InputStream);
byte[] byteArray = binReader.ReadBytes(postedFile.ContentLength);
}
}
catch (System.Exception e)
{
//TODO
}
return Request.CreateResponse(HttpStatusCode.Created);
}
Complementing Matt Frear's answer - This would be an ASP NET Core alternative for reading the file directly from Stream, without saving&reading it from disk:
public ActionResult OnPostUpload(List<IFormFile> files)
{
try
{
var file = files.FirstOrDefault();
var inputstream = file.OpenReadStream();
XSSFWorkbook workbook = new XSSFWorkbook(stream);
var FIRST_ROW_NUMBER = {{firstRowWithValue}};
ISheet sheet = workbook.GetSheetAt(0);
// Example: var firstCellRow = (int)sheet.GetRow(0).GetCell(0).NumericCellValue;
for (int rowIdx = 2; rowIdx <= sheet.LastRowNum; rowIdx++)
{
IRow currentRow = sheet.GetRow(rowIdx);
if (currentRow == null || currentRow.Cells == null || currentRow.Cells.Count() < FIRST_ROW_NUMBER) break;
var df = new DataFormatter();
for (int cellNumber = {{firstCellWithValue}}; cellNumber < {{lastCellWithValue}}; cellNumber++)
{
//business logic & saving data to DB
}
}
}
catch(Exception ex)
{
throw new FileFormatException($"Error on file processing - {ex.Message}");
}
}