22

I am not able to figure out, how to write a .NET Core Web API to support File Upload. Please note I am not using ASP.NET Core MVC form for file upload but via a Servlet/JSP container. Here is how my project.json is defined,

{ "dependencies": { "Microsoft.NETCore.App": { "version": "1.0.1", "type": "platform" }, "Microsoft.AspNetCore.Mvc": "1.0.1", "Microsoft.AspNetCore.Routing": "1.0.1", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", "Npgsql": "3.1.9", "CoreCompat.Newtonsoft.Json": "9.0.2-beta001", "Newtonsoft.Json": "9.0.1" }, "tools": { "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" }, "frameworks": { "netcoreapp1.0": { "imports": [ "dotnet5.6", "portable-net45+win8" ] } }, "buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true }, "runtimeOptions": { "configProperties": { "System.GC.Server": true } }, "publishOptions": { "include": [ "wwwroot", "**/*.cshtml", "appsettings.json", "web.config" ] }, "scripts": { "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] } } 

Here is how my Startup is defined,

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using QCService.Models; namespace QCService { public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); //Add SurveysRepository for Dependency Injection services.AddSingleton<ISurveysRepository, SurveysRepository>(); //Add FeedbacksRepository for Dependency Injection services.AddSingleton<IFeedbacksRepository, FeedbacksRepository>(); //Add AttachmentsRepository for Dependency Injection services.AddSingleton<IAttachmentsRepository, AttachmentsRepository>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseMvc(); } } } 

Finally here is how my Controller is defined,

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; //Including model classes for Attachments using QCService.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Hosting; using System.IO; using Microsoft.Net.Http.Headers; // For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 namespace QCService.Controllers { [Route("api/[controller]")] public class AttachmentsController : Controller { public IAttachmentsRepository AttachmentItems { get; set; } public AttachmentsController(IAttachmentsRepository attachmentItems) { AttachmentItems = attachmentItems; } // GET: api/Attachments [HttpGet] /*http://localhost:52770/api/Attachments*/ public IEnumerable<Attachments> Get() { return AttachmentItems.GetAllAttachments(); } // GET api/Attachments/5 [HttpGet("{id}")] /*http://localhost:52770/api/Attachments/{AttachmentID}*/ public Attachments Get(int id) { return AttachmentItems.GetAttachment(id); } // GET api/Attachments/5 [HttpGet("Feedback/{id}")] /*http://localhost:52770/api/Attachments/Feedback/{FeedbackID}*/ public IEnumerable<Attachments> GetFeedbackAttachments(int id) { return AttachmentItems.GetFeedbackAttachments(id); } // POST api/Attachments [HttpPost]/*http://localhost:52770/api/Attachments/*/ public async Task<IActionResult> PostFiles(ICollection<IFormFile> files) { try { System.Console.WriteLine("You received the call!"); WriteLog("PostFiles call received!", true); //We would always copy the attachments to the folder specified above but for now dump it wherver.... long size = files.Sum(f => f.Length); // full path to file in temp location var filePath = Path.GetTempFileName(); var fileName = Path.GetTempFileName(); foreach (var formFile in files) { if (formFile.Length > 0) { using (var stream = new FileStream(filePath, FileMode.Create)) { await formFile.CopyToAsync(stream); //formFile.CopyToAsync(stream); } } } // process uploaded files // Don't rely on or trust the FileName property without validation. //Displaying File Name for verification purposes for now -Rohit return Ok(new { count = files.Count, fileName, size, filePath }); } catch (Exception exp) { System.Console.WriteLine("Exception generated when uploading file - " + exp.Message); WriteLog("Exception generated when uploading file - " + exp.Message, true); string message = $"file / upload failed!"; return Json(message); } } /// <summary> /// Writes a log entry to the local file system /// </summary> /// <param name="Message">Message to be written to the log file</param> /// <param name="InsertNewLine">Inserts a new line</param> public void WriteLog(string Message, bool InsertNewLine) { LogActivity ologObject = null; try { string MessageString = (InsertNewLine == true ? Environment.NewLine : "") + Message; if (ologObject == null) ologObject = LogActivity.GetLogObject(); ologObject.WriteLog(Message, InsertNewLine); } catch (Exception ex) { Console.WriteLine("Unable to write to the log file : " + ex.Message); Console.WriteLine("Stack Trace : " + ex.StackTrace); } } } } 

I have gone through this link, https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads but just can't make it work! Any help is appreciated!!!

I am using the Google's Advanced Rest Client to post the data as follows, This is how I am sending the POST request

I keep getting response failed with following message... Status: 500: Internal Server Error Loading time: 62 ms Response headers 5 Request headers 2 Redirects 0 Timings Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0RKUhduCjSNZOEMN Content-Length: 9106 Source message

POST /api/Attachments HTTP/1.1 HOST: localhost:52770 content-type: multipart/form-data; boundary=----WebKitFormBoundary0RKUhduCjSNZOEMN content-length: 9106

------WebKitFormBoundary0RKUhduCjSNZOEMN Content-Disposition: form-data; name="fileUpload1"; filename="1i4ymeoyov_In_the_Wild_Testing.png" Content-Type: image/png

�PNG

IHDR,I�3(tEXtSoftwareAdobe ImageReadyq�e< iTXtXML:com.adobe.xmp �8 ^2IDATx��] pU����@ a�H� Pe�P8 ��Ȉ��b�̌3�p�q�*�7"�h�+Yf��O�atD��(<�h¦DLXdOH ������=}���}ov8_U�..... ------WebKitFormBoundary0RKUhduCjSNZOEMN--

1
  • 1
    Note that in ASP.NET Core there is no distinction between MVC and Web API (just FYI).. Commented Feb 16, 2017 at 20:29

4 Answers 4

14

This is the root cause: form-data; name="fileUpload1"

Your name "fileUpload1" has to be "files" which matches the declaration of the action PostFiles(ICollection< IFormFile> files)

Sign up to request clarification or add additional context in comments.

Comments

2

Hi Change the contentype from "multipart/form-data" to "application/octet-stream". Moved inside code block without error. Try the code below that sends the file as binary type:

byte[] fileToSend = File.ReadAllBytes(filePath); HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/octet-stream"; httpWebRequest.ContentLength = fileToSend.Length; using (Stream requestStream = httpWebRequest.GetRequestStream()) { requestStream.Write(fileToSend, 0, fileToSend.Length); requestStream.Close(); } 

Hope it will work ..Thanks

Comments

0

In the request, your URL is not correct. Check the route on the controller, it is [Route("api/[controller]")] and the action method name is PostFiles. So, the correct URL will be http://localhost:52770/api/Attachments/PostFiles

Either correct the URL or rename the action method as Index

3 Comments

I tried both ways but no luck. If I use the URL as 'localhost:52770/api/Attachments/PostFiles' I get HTTP 404 as response. If I redefine the action method as Index then I get '500: Internal Server Error' as response. Please note I have updated the name of the parameter to files when selecting files in 'Google's ARC' used for posting the request to the .NET Core Web API.
If you use the action method name as Index, are you able to get to the action method for debugging? One more thing, Path.GetTempFileName() creates a temporary file in the user’s temporary folder. The App might not have permission to do so. Try a different path var uploads = Path.Combine(_environment.WebRootPath, "uploads"); foreach (var file in files) { if (file.Length > 0) { using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create)) { await file.CopyToAsync(fileStream);} } }
Actually, api/[controller] indicates that the action is not required and is, in fact, ignored. Otherwise, it would say api/[controller]/[action]. Typically with web api's you differentiate between CRUD requests based on the HTTP verb (i.e. GET = read, POST = create, etc.) as opposed to having different routes, so if you come from an MVC background, this may be a bit confusing.
0

I think your issue is due to a bug in chrome 56.

ARC can't send files on new Chrome version: 56.0.2924.76

This issue affects both postman and ChromeRestClient

The simple workaround is enable the Use XHR option and remove the header Content-Type: multipart/form-data.

This works for both postman and chrome

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.