I am managing a web application that accepts a file from a user and afterwards uploads it to a file location in the server. In order to cater files of big sizes (100 MB above), I decided to use the blob.slice method. My problem is that after the file is uploaded and I try to download it, the file size is twice its original size, thus, causing it to be corrupted. I will be showing the flow of data from the client side up to the server side to show the step-by-step actions of the upload method.
The directive is where the HTML for the input type="file" and the logic of the blob slicing is located .
CLIENT SIDE
//Directive
var template = [
'<div class="file-input">',
'<div>',
'<input type="text" ng:model="fileinfo.meta.name" disabled />',
'<div class="filebrowse">',
'<button type="button" class="browsemodal">Browse</button>',
'<input type="file"></input>',
'</div>',
'</div>',
'</div>'
].join('');
module.exports.init = function (app) {
app.directive('fileInput', [
function () {
return {
restrict: 'E',
template: template,
replace: true,
scope: {
fileinfo : '=ngModel'
},
link: function (scope, element) {
element.bind('change', function (ev) {
var fileSize = ev.target.files[0].size;
var chunkSize = 64 * 1024;
var offset = 0;
var self = this;
var chunkReaderBlock = null;
var readEventHandler = function (evt) {
offset += evt.target.result.length;
scope.fileinfo.meta = ev.target.files[0];
scope.fileinfo.data = ev.target.files[0];
scope.fileinfo.sampleData.push(evt.target.result);
if (offset >= fileSize) {
return;
}
chunkReaderBlock(offset, chunkSize, ev.target.files[0]);
};
chunkReaderBlock = function (_offset, length, _file) {
var reader = new FileReader();
var blob = _file.slice(_offset, length + _offset);
reader.onload = readEventHandler;
reader.readAsText(blob);
};
chunkReaderBlock(offset, chunkSize, ev.target.files[0]);
});
}
}
}
]);
};
scope.fileinfo represents a property called documentInfoModel in the factory as you can see in the snippet below.
//Factory
documentInfoModel: function () {
var self = this;
self.meta = null;
self.data = null;
self.sampleData = [];
return self;
Now, as soon as I click the Upload button, it will trigger a function named saveData in the controller. This function will call an http.Post to the API from the server side through the documentService.upsertDocument method. The API is named AddFile. See full details below.
//Controller
$scope.saveData = function () {
documentService.upsertDocument($scope.fileInfoItem).then(function (data) {
//File was uploaded successfully
};
};
SERVER SIDE
public HttpResponseMessage AddFile(HttpRequestMessage request, [FromBody] DocumentInfoModel file)
{
using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 30, 0)))
{
try
{
StringBuilder sb = new StringBuilder();
foreach (string text in file.sampleData)
sb.Append(text);
byte[] data = Encoding.Unicode.GetBytes(sb.ToString());
var fileLocation = "C:\Temp\";
var targetFileName = file.data;
if (!Directory.Exists(fileLocation))
Directory.CreateDirectory(fileLocation);
File.WriteAllBytes(targetFileName, data);
}
catch()
{}
return request.CreateResponse(HttpStatusCode.OK);
}
Can anyone help me identify anything that is wrong with the code? I will be putting here as well the download API if it helps. Thanks a lot!
private HttpResponseMessage Download(string fileName)
{
var filePath = "C:\Temp\";
var res = new HttpResponseMessage();
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
res.Content = new StreamContent(File.OpenRead(filePath));
res.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
res.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
res.StatusCode = HttpStatusCode.OK;
}
else
res.StatusCode = HttpStatusCode.InternalServerError;
return res;
}