4

I need upload csv file and parse it. I can see file in http body request, but when I pass it in the csvreader I can see base64 string like headers: enter image description here

On client side I use angularjs:

'uploadBulkUsersFile': {
                method: 'POST', url: CONFIG.apiServiceBaseUri + "api/users/bulkUsers",
                headers: {
                     "Content-Type": undefined
                },
                transformRequest: angular.identity,
                withCredentials: true
            },

and call:

var _uploadBulkUsersFile = function (bulkUsersFile) {
    var fd = new FormData();
    fd.append("file", bulkUsersFile);

    return usersResource.uploadBulkUsersFile({}, fd, function (result) {
        return result;
    }).$promise;
};

and on server side I use webapi2:

    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 buffer = await file.ReadAsStreamAsync();

        TextReader textReader = new StreamReader(buffer);
        var csv = new CsvReader(textReader);
        var records = csv.GetRecords<BulkUploadUser>().ToList();

        return Created("DefaultApi", records);
    }

http request payload

------WebKitFormBoundarySKPlgJRINOMnpxVP Content-Disposition: form-data; name="file"

data:application/vnd.ms-excel;base64,RW1haWwsRmlyc3ROYW1lLExhc3ROYW1lDQpwcm9zdG8uZHVkYUBnbWFpbC5jb20yLERlbmlzLER1ZGFpZXYNCnByb3N0by5kdWRhQGdtYWlsLmNvbSxEZW5pcyxEdWRhaWV2DQpwcm9zdG8uZHVkYUBnbWFpbC5jb20yLERlbmlzLER1ZGFpZXYNCg==
------WebKitFormBoundarySKPlgJRINOMnpxVP--

UPDATE

@Ubercode suggest me convert base 64 to string, I made it, but is looks very disgusting:

var buffer = await file.ReadAsStreamAsync();

TextReader textReader = new StreamReader(buffer);
var text = textReader.ReadToEnd();
var indexOfWord = "base64,";
var base64EncodedBytes = System.Convert.FromBase64String(text.Substring(text.IndexOf(indexOfWord) + indexOfWord.Length));
var encoded = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
TextReader textReader2 = new StringReader(encoded);
var csv = new CsvReader(textReader2);
var records = csv.GetRecords<BulkUploadUser>().ToList();
Jens Meinecke
  • 2,904
  • 17
  • 20
Mediator
  • 14,951
  • 35
  • 113
  • 191

4 Answers4

1

You need to decode the base64 encoded stuff into your file:

How do I encode and decode a base 64 string

You can tidy your code up a little bit thus:

string text;
using(TextReader textReader = new StreamReader(buffer))
     text = textReader.ReadToEnd();

CsvReader csv;

using(var ms 
   = new MemoryStream(Convert.FromBase64String(text.Substring(text.IndexOf(',') + 1)))
using (var textReader2 = new StreamReader(ms))
    csv = new CsvReader(textReader2);

var records = csv.GetRecords<BulkUploadUser>().ToList();
Community
  • 1
  • 1
Jens Meinecke
  • 2,904
  • 17
  • 20
0
        if (!Request.Content.IsMimeMultipartContent())
        {
            return StatusCode(HttpStatusCode.UnsupportedMediaType);
        }

        HttpPostedFile uploadedFile = HttpContext.Current.Request.Files[0];

         var csv = new CsvReader(uploadedFile.InputStream);
0

The uploaded data is indeed csv, not excel:

Email,FirstName,LastName
prosto.duda@gmail.com2,Denis,Dudaiev
prosto.duda@gmail.com,Denis,Dudaiev
prosto.duda@gmail.com2,Denis,Dudaiev

It would be best to prevent the data being converted to base64 in the first place (it's bigger and needs decoding).

For some reason, the data is send as 'excel' and in base64:

data:application/vnd.ms-excel;base64,

So you probably want to check the following:

headers: {
    "Content-Type": undefined
},
transformRequest: angular.identity,

I don't know AngularJS, but you might want to change Content-Type to text/csv and check transformRequest.

Or maybe it gets converted to base64 here:

var fd = new FormData();
fd.append("file", bulkUsersFile);

So it would be best to check where the data gets converted and fix it to send plain text instead. This will prevent the encoding/decoding step, and the data will be smaller too.

Danny_ds
  • 11,201
  • 1
  • 24
  • 46
0

Just had the same problem, thanks for the advices in this post.

First of all, there are two possibilities to upload a file: Http-POST-Content or form-data (is post too, but not equally the same).

I'm using the simpler method, the Http-POST-Content. There are no problems with base64 encoding, etc, the solution stays simple, but I think it can be pretty uneficcent with huge files.

I used the follwing code in the angular Project:

Angular Code

Import Dialog (html):

<input hidden #imageInput type="file" accept=".csv" (change)="processFile(imageInput)">
<button mat-button class="image-upload-button full-width" (click)="imageInput.click()">
   <mat-icon>add</mat-icon>
</button>

Dialog Code (.ts)

const file: File = imageInput.files[0];
this.dialogRef.close(file);

ts-Code executed after dialog close

const url = 'myapi/Import';
return this.http.post<ImportResult>(url, file).toPromise();

C# Code

try
{
    var result = new ImportResult();
    s = System.Web.HttpContext.Current.Request.GetBufferedInputStream();
    using (var sr = new System.IO.StreamReader(s))
    {
        s = null;
        var reader = new CsvReader(sr, new CsvConfiguration
        {
            Delimiter = ";"
        });
        var records = reader.GetRecords<RowEntry>();
        ...
    }
    return result;
}
finally
{
    s?.Dispose();
}

Hope this will help, it's a simple solution working fine.

Oswald
  • 1,252
  • 20
  • 32