0

I have an MVC cross Web API app that has an ApiFileController, headed:

[Produces("application/json")]
[Route("api/File")]
public class ApiFileController : ApiBaseController

It has the following action method

[HttpPost("PostDir")]
[DisableRequestSizeLimit]
public async Task<IActionResult> PostDir(string serverPath)
{
    using (var fileStream = new FileStream(serverPath, FileMode.Create, FileAccess.Write))
    {
        await Request.Body.CopyToAsync(fileStream);
    }

    return Ok();
}

that is supposed to received a zipfile containing a directory, and unzip it into the serverPath parameter. Yet when I try and post a file as follows:

sourcePath = Path.Combine("Temp", Guid.NewGuid() + ".zip");
System.IO.Compression.ZipFile.CreateFromDirectory(localPath, sourcePath, CompressionLevel.Fastest, true);
...
using (var fileStream = File.Open(sourcePath, FileMode.Open, FileAccess.Read))
{
    using (var reader = new StreamReader(fileStream))
    using (var content = new StreamContent(reader.BaseStream))
    {
        var uri = $"api/File/PostDir?serverPath={WebUtility.UrlEncode(serverPath)}";
        var resp = await _client.PostAsync(uri, content);
        resp.EnsureSuccessStatusCode();
    }
}

I get a 404 - Not found. If I post a plain text file, as follows,

    using (var fileStream = File.Open(localPath, FileMode.Open, FileAccess.Read))
    {
        using (var reader = new StreamReader(fileStream))
        using (var content = new StreamContent(reader.BaseStream))
        {
            var uri = $"api/File/PostDir?serverPath={WebUtility.UrlEncode(serverPath)}";
            var resp = await _client.PostAsync(uri, content);
            resp.EnsureSuccessStatusCode();
        }
    }

where localPath points to a plain text file, the PostDir action is correctly invoked and properly saves the text file.

I am using HttpClient, in a wrapper class, to make the requests, and it is initialized in the wrapper's ctor as follows:

public ApiClient()
{
    var baseAddress = ConfigurationManager.AppSettings["ApiUrl"];
    _client = new HttpClient();
    _client.BaseAddress = new Uri(baseAddress);
    _client.DefaultRequestHeaders.Clear();
    _client.DefaultRequestHeaders.ConnectionClose = false;
    _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}

I suspect that when posting the binary zip files, I am missing a content type header or such. What am I doing wrong?

Developer Thing
  • 2,704
  • 3
  • 21
  • 26
ProfK
  • 49,207
  • 121
  • 399
  • 775
  • Any particular reason you are posting the file in the body? – Aman B Jun 06 '18 at 09:17
  • @AmanB I am new to `HttpClient`, and all examples I have successfully followed, and all my working code based on them, post the file in the body. Simple as that. – ProfK Jun 06 '18 at 09:21
  • 2
    In that case, try posting request body as `MultipartFormDataContent` and then you can access the files using `Request.Files` collection on the receiving end. see https://stackoverflow.com/questions/1131425/send-a-file-via-http-post-with-c-sharp – Aman B Jun 06 '18 at 09:22
  • Check/set the content type of the stream being sent to the server. By default it will accept text. – Nkosi Jun 06 '18 at 09:27
  • @Nkosi I don't know exactly what header and what value to set for the content type with this zip file. – ProfK Jun 06 '18 at 09:29
  • 1
    The action method code you've shown is called "PostDir" The URL you appear to be calling is called "PostFile" Have you shown us the wrong action method? Anyway as the others have pointed out, you can't send binary data (i.e. almost any type of file which isn't text) without setting the correct content type in your request. – ADyson Jun 06 '18 at 09:33
  • Try setting the content type for the zip file before posting`content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");` – Nkosi Jun 06 '18 at 09:37
  • @ADyson That was a typo on my part. I had copied code to notepad before changing it, then posted the code from notepad. Anyway, I have now grokked that I have to set a correct content type header, and even before editing, the last part of my question was just how to do so. – ProfK Jun 06 '18 at 09:40
  • @Nkosi Nope, with that header, or `application/octet-stream`, I still get a 404. – ProfK Jun 06 '18 at 09:44

1 Answers1

-1

Don't take my code as gospel on how to upload a zip file, but it turns out the error was that I had only used the [DisableRequestSizeLimit] attribute on the action method, and that only disables Kestrel's request size limit. IIS still has a 30MB limit which I disabled by adding a web.config with the following:

<system.webServer>
  <security>
    <requestFiltering>
      <!-- 1 GB -->
      <requestLimits maxAllowedContentLength="1073741824" />
    </requestFiltering>
  </security>
</system.webServer>
ProfK
  • 49,207
  • 121
  • 399
  • 775