1

The "normal" way of accepting file uploads in Web API is to examine the the Request.Content. However, I'd like to have the file data be an actual parameter of the method, so that tools like NSwag are able to generate proper TypeScript clients.

So instead of stuff like this:

    [HttpPost]
    public async Task UploadFile()
    {            
        var provider = new MultipartFormDataStreamProvider(Path.GetTempPath());
        var result = await Request.Content.ReadAsMultipartAsync(provider);
        var formData = result.FormData.GetValues("Path");
        ...
    }

I just want something like this:

 public async Task UploadFileInfo(Models.FileInfo fileInfo)
 {
     //FileInfo contains a string of the binary data, decode it here
 }

I have this partly working except that I can't reliably encode the binary file data as a string in JavaScript and then decode it for saving in C#/.NET.

In JavaScript, I've tried FileReader.readAsArrayBuffer, FileReader.readAsBinaryString, FileReader.readAsDataURL, FileReader.readAsText, but none of these have produced a string of binary data that I can reliably decode. In C#, I've tried System.Text.Encoding.UTF8.GetBytes and/or Convert.FromBase64String.

For reference, the relevant code NSwag is generating to communicate with the Web API is as follows:

    const content_ = JSON.stringify(fileInfo);

    let options_ : any = {
        body: content_,
        observe: "response",
        responseType: "blob",
        headers: new HttpHeaders({
            "Content-Type": "application/json", 
        })
    };

    return this.http.request("post", url_, options_)

where this.http is an Angular HttpClient.

MgSam
  • 12,139
  • 19
  • 64
  • 95
  • 1
    You can convert the file blob to a base-64 encoded string in both JavaScript and C#. – Jasen Mar 19 '18 at 20:25
  • In our applications we did so, but notice the base64 related size "overhead" – DotNetDev Mar 19 '18 at 20:57
  • @Jasen Can you post code showing the client and web API code? I've tried a bunch of combinations of the FileReader methods but can't get it to work. – MgSam Mar 19 '18 at 21:04
  • @DotNetDev ditto. – MgSam Mar 19 '18 at 21:04
  • when I'm at work tomorrow I check our source – DotNetDev Mar 19 '18 at 21:08
  • Here's a [JavaScript conversion](https://stackoverflow.com/questions/36280818/how-to-convert-file-to-base64-in-javascript). Then you treat it like a string for the transport request. Your controller will need to [convert the string param back into a blob](https://stackoverflow.com/questions/13178737/convert-base64-to-image-in-c-sharp). – Jasen Mar 19 '18 at 21:19
  • @Jasen JS's `FileReader.readAsDataUrl()` and .NET's `Convert.FromBase64String()` aren't properly encoding/decoding in a format that each other understands. I get `The input is not a valid Base-64 string as it contains a non-base 64 character...` errors. – MgSam Mar 20 '18 at 12:59
  • Scratch my previous comment. It works if you do `source.Substring(source.IndexOf(',') + 1)` first. Thanks for the help. – MgSam Mar 20 '18 at 13:49

1 Answers1

0

FileDto

[DataContract]
public class RestFileDataDto
{
    [DataMember(Name = "data", IsRequired = true)]
    [Required]
    public string Data { get; set; }

    [DataMember(Name = "filename")]
    public string Filename { get; set; }
}

Base64-Persistence

public void SaveFile(string data, string filePath)
{
    var dir = Path.GetDirectoryName(filePath);
    if (!Directory.Exists(dir))
    {
        Directory.CreateDirectory(dir);
    }

    using (FileStream outputStream = File.Create(filePath))
    {
        using (Stream inputStream = new MemoryStream(new Base64Converter().ConvertToByteArray(data)))
        {
            inputStream.Seek(0, SeekOrigin.Current);
            inputStream.CopyTo(outputStream);
        }
    }
}

Controller

[HttpPut]
[Route("route")]
public HttpResponseMessage Upload([FromBody] RestFileDataDto fileImage)
{
    //// call persistence
}

Base64Converter().ConvertToByteArray

public byte[] ConvertToByteArray(string base64String)
{
    var splittedString = base64String.Split(',');
    return Convert.FromBase64String(splittedString[splittedString.Length - 1]);
}
DotNetDev
  • 205
  • 1
  • 10
  • Is your `new Base64Converter()` supposed to be `Convert.FromBase64String()`? .NET doesn't have a class called `Base64Converter` as far as I can tell. – MgSam Mar 20 '18 at 12:56
  • When I use `FileReader.readAsDataURL()` on the JavaScript side and `Convert.FromBase64String()` on the .NET side I get the `The input is not a valid Base-64 string as it contains a non-base 64 character...` error. So that method doesn't work. – MgSam Mar 20 '18 at 12:56
  • sry, yes its a not a .NET Class, I'll update the code – DotNetDev Mar 22 '18 at 07:23