We have an application that allows users to set themes for a subdomain within our website and this includes some configuration data and the ability to upload their logo to be displayed at the top of the page. So basically I'm going to be sending a single uploaded image file as well as some additional data entered in a form as a single request and have it processed at the server. From the research I've done, processing file uploads in AngularJS isn't trivial.
This application was originally using ASP.NET Web Forms but we're rewriting it in AngularJS and Web API and I am not completely fluent in either just yet. I know that I can send files as byte arrays but back in the days of Web Forms, a lot of stuff was hidden from the developer and now I actually need to understand what's happening under the hood.
I've been able to write some code that appears to acquire the file bytes and append them to a scoped object which is being passed to the API. Without the file upload data, all of this works great. It's when I add the file upload data, I have a problem. I'm using a FileReader object to get the byte array (or the buffer, are they the same thing?) for the uploaded image. However, when I send it to the API the image data is empty and that leads to an exception. Obviously I'm not doing something right.
I have two concerns here. First, we have a service using resources that is used for communicating with the API. I don't know if I can modify the headers for the particular save operation. Code is below.
According to what I've read, all this needs to happen in a POST operation, however, if I'm modifying existing data I'm using a PUT operation and I am concerned this isn't going to work. I can't say for sure if this is actually happening because I can't even get the image data to make it to the API.
function loadImageFile(e) {
var file = e.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function () {
var customerLogo = {
imageBytes: reader.result,
filename: file.name
};
$scope.theme.customerLogo = customerLogo;
};
reader.readAsArrayBuffer(file);
}
else
$scope.theme.customerLogo = null;
}
<input id="uploadCustomerLogo" accept="image/*" class="form-control" name="uploadCustomerLogo" type="file" />
function saveTheme() {
if ($scope.themeDetailForm.$valid) {
var onSuccess = function (response) {
refresh();
};
var onError = function (response) {
$window.alert('An error occurred while saving the Theme.');
};
if (!$scope.theme.id)
appFactory.themes.create($scope.theme).$promise.then(onSuccess, onError);
else
appFactory.themes.update({ id: $scope.theme.id }, $scope.theme).$promise.then(onSuccess, onError);
}
}
angular
.module('app')
.factory('appFactory', appFactory);
appFactory.$inject = ['$rootScope', '$resource', 'configs'];
function appFactory($rootScope, $resource, configs) {
var createOperation = { method: 'POST' };
var getOperation = { method: 'GET' };
var queryOperation = { method: 'GET', isArray: true };
var removeOperation = { method: 'DELETE' };
var updateOperation = { method: 'PUT' };
var themes = $resource(buildResourceUrl('Themes/:id'), { id: '@id' }, {
create: createOperation,
get: getOperation,
query: queryOperation,
remove: removeOperation,
update: updateOperation
});
return { themes: themes };
}
function buildResourceUrl(resourcePath) {
return configs.apiBaseUrl + resourcePath;
}
I know there are some directives on the web but I've been hesitant to use them because I am unsure how to integrate them with the "appFactory" shown above. What do I need to do to get this working? I have to get this finished this week.
The article at AngularJS: how to implement a simple file upload with multipart form? mentions a way to do this but the file is uploaded immediately and this is not the behavior I want. I need to be able to send the file at the same time as the other data. I realized that usign the file reader the result is an ArrayBuffer and I need to convert it to a byte array, and I do that by using new Uint8Array(reader.result) but when I receive the data at the server, the byte array has nothing in it.