------WebKitFormBoundary being added by AngularJS app - angularjs

I built a Ruby/Sinatra backend for an angularjs app. I wrote a POST call to parse and upload a file from the request body to AWS S3. Now when I use Postman to upload a file. It works completely fine, the file gets uploaded and I can view it on S3. But the problem is that when I try to upload a file using the angular app, it adds the following to the file cause of which the file gets corrupted and a pdf file opens up as a text file within the browser.
------WebKitFormBoundaryuRzuSgC6oXxEgwa1
Content-Disposition: form-data; name="file"; filename="Application Form.pdf"
Content-Type: application/pdf
***FILE DATA HERE***
------WebKitFormBoundaryuRzuSgC6oXxEgwa1--
Can someone help me to either parse this data properly in the ruby or help me to avoid adding this extra code from the angular app.
This is the angular code to upload the file
$scope.uploadFiles = function(files, errFiles) {
$scope.all_files.push(files);
console.log($scope.all_files[0]);
$scope.files = files;
$scope.errFiles = errFiles;
angular.forEach(files, function(file) {
file.upload = Upload.upload({
url: 'http://api.company.com/upload',
data: {file: file}
});
file.upload.then(function (response) {
$timeout(function () {
file.result = response.data;
});
}, function (response) {
if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
}, function (evt) {
file.progress = Math.min(100, parseInt(100.0 *
evt.loaded / evt.total));
});
});
};
And the following is the code in Ruby to parse and upload the file
post '/upload' do
request.body.rewind
body = request.body.read
res(200, {url: saveFile(body)})
end
def saveFile(body)
name = "random string here"
s3 = Aws::S3::Resource.new(region:'ap-southeast-1')
obj = s3.bucket('company-files').object(name)
obj.put(body: body, acl:'public-read')
saved_url = obj.public_url
return saved_url
end

You may need to manually set the Content-Type header to force your browser to use this upload mechanism, probably at this point:
file.upload = Upload.upload({
url: 'http://api.company.com/upload',
data: {file: file}
});
I don't know what's Upload object, so I can't say how to do it

Related

Change Content-Type for ng file upload

I am trying to upload files with ng-file-upload. I have this working for the most part, but there is a bug in the API that's being called which means that .xlsx files do no upload properly. Other file types do work. I have been advised that forcing the Content-Type in the payload to 'application/x-zip-compressed' allows the file to upload. Unfortunately, I cannot figure out how to change this. The payload looks like this:
------WebKitFormBoundary9rVv6WxE2BM7vFxz
Content-Disposition: form-data; name="file[0]"; filename="My Excel File.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
------WebKitFormBoundary9rVv6WxE2BM7vFxz--
but needs to look like this:
------WebKitFormBoundary9rVv6WxE2BM7vFxz
Content-Disposition: form-data; name="file[0]"; filename="My Excel File.xlsx"
Content-Type: application/x-zip-compressed
------WebKitFormBoundary9rVv6WxE2BM7vFxz--
Can anyone help with how to make this change? I have tried changing the type property on the file object, but that hasn't made the change.
The function that does the upload is
$scope.uploadThenRun = function (file) {
Upload.upload({
url: 'https://'+ ppService.ppServer+ ':'+ ppService.sslPort + '/jobs/',
data: {file: file}
}).then(function (resp) {
var jobID = resp.data.data.value;
$scope.loadParentFormulation(jobID);
}, function (resp) {
}, function (evt) {
});
Which is called from a div like so:
<div class="my-drop-zone">
<div ngf-drop="uploadThenRun($files)" class="drop-box well"
ngf-drag-over-class="'dragover'" ngf-multiple="true">Drop your file here</div>
</div>
Here's the network request that's sent. It's the content-type in the payload that needs to change.
Hope someone can help
You can change the file type before uploading like this:
if (file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
file = file.slice(0, file.size, "application/x-zip-compressed")
}
Upload.upload({..., data: {file: file}, ...})
This would make your file of the new type with the same content.
you can define Content-type using headers.
$scope.uploadThenRun = function (file) {
Upload.upload({
url: 'https://'+ ppService.ppServer+ ':'+ ppService.sslPort + '/jobs/',
data: {file: file},
headers : {'Content-Type': 'application/x-zip-compressed'}
}).then(function (resp) {
var jobID = resp.data.data.value;
$scope.loadParentFormulation(jobID);
}, function (resp) {
}, function (evt) {
});

nodejs write simple image blob - Upload.dataUrltoBlob

Simple question. How do I save a image blob in Nodejs from angular.
AngularSide:
$scope.upload = function (dataUrl, picFile) {
Upload.upload({
url: 'http://test.dev:3000/register/user/uploads',
data: {
file: Upload.dataUrltoBlob(dataUrl, picFile.name)
},
}).then(function (response) {
$timeout(function () {
$scope.result = response.data;
});
}, function (response) {
if (response.status > 0) $scope.errorMsg = response.status
+ ': ' + response.data;
}, function (evt) {
$scope.progress = parseInt(100.0 * evt.loaded / evt.total);
});
}
nodejs side: Do I need middleware here? if so which one should I use?
router.post('/user/uploads', multipartMiddleware, function(req, resp) {
var newPath = "/Users/testUser/test_hold_files/" + req.files.file.originalFilename;
fs.writeFile(newPath, req.files.file, function(err) {
if (err) {
console.log("Data Error ");
return console.error(err);
}
});
res.status(200).jsonp({status: "status: success "});
});
right now this just writes out the file with correct name but its empty.
You used to be able to access the uploaded file through req.files.imageName and then you would fs.readFile from tmp and write it permanently, which is no longer the case in express 4.0
In Express 4, req.files is no longer available on the req object by default. To access uploaded files on the req.files object, use multipart-handling middleware like busboy, multer, formidable, multiparty, connect-multiparty, or pez.
Soooooooo, you can feel free to use which ever one of those middlewares names above and then follow their API for dealing with uploaded files like images. Hope this helps, enjoy.
Ok,
After a long time of messing with this stuff. I found an answer. It does load the file in my folder.
I feel this is only partial since it does not resize the actual file smaller. It is what is selected with https://github.com/danialfarid/ng-file-upload. I used the
Upload.upload({
url: 'http://test.dev:3000/register/user/uploads',
data: {
file: Upload.dataUrltoBlob(dataUrl, picFile.name)
},
This did zoom into the file on selected image. It did not make the actual file size smaller. I am still looking into this issue.
var formidable = require('formidable'),
util = require('util'),
fs_extra = require('fs-extra');
This is my post to accept images.
router.post('/user/uploads', function (req, res){
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
form.on('end', function(fields, files) {
/* Temporary location of our uploaded file */
var temp_path = this.openedFiles[0].path;
/* The file name of the uploaded file */
var file_name = this.openedFiles[0].name;
/* Location where we want to copy the uploaded file */
var new_location = "/Users/testUser/test_hold_files/";
fs_extra.copy(temp_path, new_location + file_name, function(err) {
if (err) {
console.error(err);
} else {
console.log("success!")
}
});
});
});
I have also noticed that I can view the file in chrome but not load it into gimp. Gimp gives me a file error.
Small steps I guess.
Maybe Datsik can give us some insight on what is going on here.
https://stackoverflow.com/users/2128168/datsik
Phil

Is it possible to send base64 image to an amazon s3 bucket via ngFileUpload post?

I have a working script that when the file input changes, pick the files, sign their upload data in my django backend, and upload it from the frontend directly to my s3 bucket. It's working great. I'm using ng-file-upload to do it.
var doSignUrl = function(image, success, error){
$http({
url: scope.signUrl,
params: {
s3_object_name: image.name,
s3_object_type: image.type
},
method: 'get'
}).success(success).error(error);
};
var getUploadConfig = function(image, data){
return {
url: data.url,
method: 'POST',
fields : {
key: data.path,
AWSAccessKeyId: data.aws_access_key_id,
acl: data.acl,
policy: data.policy,
signature: data.signature,
"Content-Type": image.type != '' ? image.type : 'application/octet-stream',
filename: data.file_name
},
file: image,
};
};
var doUpload = function(image){
doSignUrl(image, function(signData){
Upload.upload(getUploadConfig(image, signData)).progress(function(e){
doProgress(image, parseInt(100.0 * e.loaded / e.total))
}).success(function(data, status, header, config){
doSuccess(image, signData.url+'/'+signData.path);
}).error(function(data, status, header, config){
doError(image);
});
}, function(data, status, header, config){
console.log(status);
console.log(data);
});
}
for each file the file picker selects i call doUpload(file)
But my real objective is to crop the image in frontend using canvas before to upload. The problem is that when you crop image using canvas, the result is a base64 encoded image, not a file. So my final question is: is it Possible to upload this base64 image directly to s3?
With a lot of research i found out that you can send a blob instead of a file unsing ngFileUpload to s3.
I used this library to convert my base64 to a blob, and then passed the generated blob instead of the file in the Upload.upload() file parameter.

angular file upload+sails skipper upload no file

I'm using angularJS for my frontend and sails.js/expressjs for my backend. I'm implementing a file upload function using ng-file-upload, and strangely the file never seem to upload to the server successfully... this is my angularJS code:
$scope.$watch('data.chosenImageFile', function() {
if ($scope.data.chosenImageFile) {
console.log($scope.data.chosenImageFile);
$scope.upload = $upload.upload({
url: '/upload_user_avatar',
method: 'POST',
file: $scope.data.chosenImageFile
}).progress(function(evt) {
console.log('progress: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
console.log(data);
});
}
});
And this is my sails.js code:
uploadUserAvatar: function(req, res) {
req.file('avatar').upload(function(err, files) {
if (err) return res.send(500, err);
return res.json({
message: files.length + ' file(s) uploaded successfully!',
files: files
});
});
},
And I always get the following response from server:
Object {message: "0 file(s) uploaded successfully!", files: Array[0]}
When I check the corresponding server upload destination folder, there's nothing.... anyone know why or could provide some help? Will really appreciate it!
Ok this question isn't a really smart one, I figured out where the problem is: the default fileFormDataName for ng-file-upload is "file", since I'm using req.file('avatar') on the server side, I should really add the following setting option in my angular code:
fileFormDataName: 'avatar',
which makes it look like so:
$scope.$watch('data.chosenImageFile', function() {
if ($scope.data.chosenImageFile) {
console.log($scope.data.chosenImageFile);
$scope.upload = $upload.upload({
url: '/upload_user_avatar',
method: 'POST',
fileFormDataName: 'avatar',
file: $scope.data.chosenImageFile
}).progress(function(evt) {
console.log('progress: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
console.log(data);
});
}
});
UPDATE
According to #Anupam Bhaskar's request, I've also added my HTML code for a file upload dropzone below:
<div ng-file-drop ng-if="!data.isUploading" ng-model="data.chosenImageFile" class="avatar-dropzone pull-left" drag-over-class="upload-dropzone" ng-file-change="avatarUpload.fileSelected('/upload_user_avatar', data.user, data)" multiple="false" allow-dir="true" accept="*">
<div class="text-center upload-sign">+</div>
</div>

Save a file in angular from a http response

I was wondering how I can save a file that is contained in a response from the server in angular ? (So that the file is automatically downloaded when the response arrives)
Edit :
I have a $http post method, and I get pdf data in the response. On success, I want to save the response data as a pdf file.
E. g :
$http({
method: 'POST',
url : 'theUrl',
data: //some array that is received
headers : //content type info
}
.success(function(response) { // I want to save the response as a pdf });
On angular 2... you can do:
import { saveAs } from 'browser-filesaver/FileSaver.js'
downloadFile(data: Response) {
var blob = new Blob([data], {type: 'application/x-tar'});
saveAs(blob, "report.tgz");
}
Using HTML5 FileSaver interface, this can be achieved:
https://github.com/eligrey/FileSaver.js/
Example solution:
//Call API to retrieve file stream using POST request
$http.post("URL", searchData, { responseType: 'arraybuffer' }).then(
response => {
//Download file from response
saveFileAs(response);
},
data => {
//raise error
}
);
function saveFileAs(response) {
var contentDisposition = response.headers("content-disposition");
//Retrieve file name from content-disposition
var fileName = contentDisposition.substr(contentDisposition.indexOf("filename=") + 9);
fileName = fileName.replace(/\"/g, "");
var contentType = response.headers("content-type");
var blob = new Blob([response.data], { type: contentType });
saveAs(blob, fileName);
}
You can't save the document as you don't have access to the users file system in a browser. You could send the URL of the pdf back, then trigger the browsers build in file save / open mechanism by adding a dummy iFrame to the body:
$http({
method: 'POST',
url : 'theUrl',
data: //some array that is received
headers : //content type info
}
.success(function (data) {
if ($('#iframe').length == 0) {
var $iframe = $('<iframe id="iframe" style="display: none"></iframe>');
$('body').append($iframe);
}
$('#iframe').attr('src', {{url to retrieve the file}})
})

Resources