upload dynamically generated pdf from aws-lambda into aws-s3 - angularjs

In my serverless app, I want to create pdf which is generated dynamically and then upload that created pdf into aws s3. My problem is, when a url is returned to client-side code from server, uploaded url doesn't working. My code is given below:
Client-side javascript code (angular.js)
$scope.downloadAsPDF = function() {
// first I have to sent all html data into server
var html = angular.element('html').html(); // get all page data
var service = API.getService();
service.downloadPdf({}, { html : html }, // api call with html data
function(res) {
console.log("res : ", res);
window.open(res.url); // open uploaded pdf file
// err: The server replies that you don't have permissions to download this file
// HTTP/1.1 403 Forbidden
}, function(err) {
console.log("err : ", err);
});
};
Serverless Code
var fs = require('fs');
var pdf = require('html-pdf');
var AWS = require("aws-sdk");
var s3 = new AWS.S3();
module.exports.handler = function(event, context) {
if (event.html) { // client html data
AWS.config.update({
accessKeyId: 'xxxxxxxxxxxxxxxxx',
secretAccessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
region: 'xxxxxxxxxxxxxxxxxxxx'
});
var awsInfo = {
bucket: 'xxxxx-xxxxxx'
};
var baseUrl = 'https://s3-my-region.amazonaws.com/s3-upload-directory';
var folderRoot = 'development/pdf';
// unique file name
var output_filename = Math.random().toString(36).slice(2) + '.pdf';
// file created directory
var output = '/tmp/' + output_filename;
pdf.create(event.html, options).toStream(function(err, stream) {
if( err ) {
console.log('pdf err : ', err);
} else {
writeStream =fs.createWriteStream(output);
s3.putObject({
Bucket : awsInfo.bucket,
Key : folderRoot + '/' + output_filename,
Body : fs.createReadStream(output),
ContentType : "application/pdf"
},
function(error, data) {
if (error != null) {
console.log("error: " + error);
} else {
// upload data: { ETag: '"d41d8cd98f00b204e9800998ecf8427e"' }
console.log('upload data : ', data);
return cb(null, {
// return actual aws link, but no file
// ex: 'https://s3-my-region.amazonaws.com/s3-upload-directory/output_filename.pdf
url: baseUrl + '/' + output_filename
});
}
});
}
}
};

I've solve my problem. I was trying to upload pdf before I generate pdf. I have solve this problem using the following code:
pdf.create(event.html, options).toStream(function(err, stream) {
if (err) {
console.log('pdf err : ', err);
} else {
var stream = stream.pipe(fs.createWriteStream(output));
stream.on('finish', function () {
s3.putObject({
Bucket : awsInfo.bucket,
Key : folderRoot + '/' + output_filename,
Body : fs.createReadStream(output),
ContentType : "application/pdf"
},
function(error, data) {
if (error != null) {
console.log("error: " + error);
return cb(null, {
err: error
});
} else {
var url = baseUrl + '/' + output_filename
return cb(null, {
url: url
});
}
});
});
}
});

I have done similar kind of thing before. I want a few clarifications from you and then I will be able to help you better.
1) In your code (server side), you have mentioned in the callback function that actual aws link is getting returned.
Are you sure that your file is getting uploaded to Amazon s3. I mean did you check your bucket for the file or not?
2) Have you set any custom bucket policy on Amazon s3. Bucket policy play an important role in what can be downloaded from S3.
3) Did you check the logs to see exactly which part of code is causing the error?
Please provide me this information and I think the I should be able to help you.

if we don't want to upload at s3 just return generated file from aws-lambda.

Related

Download File in Angular 2 (Download Pop-up not coming)

I have a view button and on click I am calling Web API to download the word document file.
WebAPI is working fine, when I paste the download URL in browser (for example http://localhost:50963/api/Download/1022), browser is showing a pop-up to save/cancel.
I wanted to have the same behavior, i.e. when user click on View button, I need to show above download pop-up. API is getting called successfully, see below screenshot
download.service.ts
export class DownloadService {
constructor(private http: Http) {}
private downloadUrl = 'http://localhost:50963/api/Download/';
//Fetch all existing Templates
DownloadDocument(Doc_Id: number){
return this.http.get(this.downloadUrl + Doc_Id.toString())
}
}
document-list.component.ts
DownloadArticle(Doc: ArticleModel){
console.log("inside downloadarticle()",Doc.Doc_Id);
this.downloadservice.DownloadDocument(Doc.Doc_Id)
.subscribe(
err => {
console.log(err);
});
}
You need to do some workaround here. Angular 'http' service cannot give what you want. I pasted all my workable code here. You need to pick the part of your need.
retrieveJquery(fileId: number, fileName: string): void {
let that = this;
let useBase64 = false;
let iOS = false;
if (navigator.platform)
iOS = /iPad|iPhone|iPod/.test(navigator.platform);
else if (navigator.userAgent)
iOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
let android = false;
if (navigator.platform)
android = /android/.test(navigator.platform);
else if (navigator.userAgent)
android = /android/.test(navigator.userAgent);
//useBase64 = iOS;
if (iOS || android) {
window.open('cloud/api/file/retrieve?fileId=' + fileId + '&base64=-1&_access_token=' + this.utilsSvc.getToken(), '_blank');
}
else {
$.ajax({
type: "GET",
headers: { 'Authorization': this.utilsSvc.getToken() },
url: 'cloud/api/file/retrieve',
data: {
'fileId': fileId,
'base64': useBase64 ? '1' : '0'
}
,
xhrFields: {
responseType: 'blob'
}
}).fail(function (jqXHR, textStatus) {
if (jqXHR.status === 501)
alert('Please configure service url first.');
else if (jqXHR.status === 404)
alert('File not found');
else
alert('Retrieving file failed: ' + textStatus + " : " + jqXHR.responseText);
}).done(function (data) {
if (useBase64)
window.open('data:' + that.utilsSvc.getMimeType(fileName) + ';base64, ' + data, '_blank');
else {
let blob = new Blob([data]);
if (window.navigator && window.navigator.msSaveOrOpenBlob)
window.navigator.msSaveOrOpenBlob(blob, fileName);
else {
let link = document.createElement('a');
link.target = '_blank';
link.href = window.URL.createObjectURL(blob);
link.setAttribute("download", fileName);
link.click();
}
}
});
}
}

Angularjs to download a private video file and play from s3 bucket

I am trying to download a video file from s3 bucket and show it in my angularjs app. I am trying to do it using AWS Node.js but not able to do it. Please help me
Sample Code
var AWS = require('aws-sdk');
AWS.config.update(
{
accessKeyId: ".. your key ..",
secretAccessKey: ".. your secret key ..",
}
);
var s3 = new AWS.S3();
s3.getObject(
{ Bucket: "my-bucket", Key: "path/to/videofile" },
function (error, data) {
if (error != null) {
alert("Failed to retrieve an object: " + error);
} else {
alert("Loaded " + data.ContentLength + " bytes");
// do something with data.Body
}
}
);
I am able to get the data but not know how to show it, data is an object
The data you're looking for will be in the Body of the response object you received.
Per AWS SDK documentation, this is what you might expect to see in Body:
Body — (Buffer, Typed Array, Blob, String, ReadableStream) Object data.
Got my answer, I need to Body element then convert it to base64 encoded data. Code below
var d = s3.getObject(params,function (error, data) {
if (error != null) {
console.log("Failed to retrieve an object: " + error);
} else {
console.log(data);
$scope.learningVideo = "data:video/mp4;base64," + encode(data.Body);
}
})
And for encode method
function encode(data)
{
var str = data.reduce(function(a,b){ return a+String.fromCharCode(b) },'');
return btoa(str).replace(/.{76}(?=.)/g,'$&\n');
}

Downloading from S3 in node and opening in a new window

My Angular 1 application saves files to S3 and allows for a wide variety of files types.
When I retrieve the objects I use the following code:
export function show(req, res) {
const s3 = new aws.S3();
const s3Params = {
Bucket: S3_BUCKET,
Key: req.query.key + ''
};
res.attachment(req.query.key + '');
var fileStream = s3.getObject(s3Params).createReadStream();
fileStream.pipe(res);
}
I would like to open the received file on the client in a new window (just like on the AWS console) but I can't figure out how to go about it.
For example on the client side does not work at all:
.then(
(data) => {
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
)
I really don't understand how the concept of data streams works.
If you don't have to download pdf, you may open it directly from s3.
s3client.getResourceUrl("your-bucket", "some-path/some-key.jpg");
This will return you url to the file.
So you need code like:
export function show(req, res) {
this.s3client = new aws.S3({
accessKeyId: options.accessKeyId,
secretAccessKey: options.secretAccessKey,
region: options.region
})
let resourceUrl = s3client.getResourceUrl(S3_BUCKET, req.query.key + '');
window.open(resourceUrl, '_blank');
}
I'm sorry, can't test it right now, but try. Should work.
All I had to do was get a signedUrl for the resource for this to work much simpler than what I was trying to do.
export function show(req, res) {
const s3 = new aws.S3();
const s3Params = {
Bucket: S3_BUCKET,
Key: req.query.key + ''
};
s3.getSignedUrl('getObject', s3Params, (err, data) => {
if (err) {
console.log(err);
return res.end();
}
const returnData = {
signedRequest: data,
};
res.write(JSON.stringify(returnData));
res.end();
});
}
and on the client all I have to do is open the link in a new tab:
openDoc(doc) {
this.$http()
.then(
(data) => {
this.$window.open(data.data.signedRequest, '_blank')
}
)
.catch(
(err) => {
this.Notification.error('failed to download attachment');
}
)
}

Delete cacheDirectory cache cordova file system ionic

There is a mobile app in which i want to delete the cached images downloaded by a particular user after log out.Suppose user1 logged in and download few images and user2 logged in and downloaded few images.User2 should not see downloaded images of any other user.
downloadFile : function(downloadLink, downloadFileName, downloadFileMimeType) {
$ionicLoading.show({
template: '<ion-spinner></ion-spinner>'
});
var accessToken = $window.localStorage.getItem(SYSTEM.AUTH_TOKEN);
var options = {
headers : {
'Authorization' : 'Bearer ' + accessToken
}
};
var ext;
if (downloadFileMimeType == 'application/pdf') {
ext = '.pdf';
} else {
ext = '.jpg';
}
var localPath;
if(ionic.Platform.isAndroid()){
localPath = cordova.file.externalCacheDirectory;
}else{
localPath = cordova.file.cacheDirectory;
}
localPath = localPath + downloadFileName.trim().replace(/\s+/g, '-') + ext;
var ft = new FileTransfer();
ft.download(downloadLink, localPath, function(entry) {
$ionicLoading.hide();
console.log("Downloading report on path - " + entry.toURL());
cordova.plugins.fileOpener2.open(entry.toURL(), downloadFileMimeType, {
error : function(e) {
console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
},
success : function(fileEntry) {
console.log('File opened successfully');
}
});
}, function fail(error) {
$ionicLoading.hide();
console.log("Error while downloading report with error code - " + error.code);
}, true, options);
}
You can store files downloaded by specific user under a user specific folder in device and delete the same when the other user logs in. Else you can follow some file naming convention specific to the users while storing the files in folder and delete those files specific to particular user when the other user logs in. The sample snippet for directory deletion and its contents using cordova file plugin is as follows:
function clearDirectory() {
if (ionic.Platform.isAndroid()) {
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, onFileSystemDirSuccess, fail);
} else {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemDirSuccess, fail);
}
function onFileSystemDirSuccess(fileSystem) {
var entry = "";
if (ionic.Platform.isAndroid()) {
entry = fileSystem;
} else {
entry = fileSystem.root;
}
entry.getDirectory("DIRECTORY_TO_DELETE", {
create: true,
exclusive: false
},
function(entry) {
entry.removeRecursively(function() {
console.log("Remove Recursively Succeeded");
}, fail);
}, getDirFail);
}
function getDirFail(error) {
navigator.notification.alert("Error");
};
function fail(error) {
navigator.notification.alert("Error");
};
}

How do you upload an image file to mongoose database using mean js

I am new to the mean stack. I want to know how to upload an image file to the database(mongoose) through angularjs. If possible, please provide me with some code. I have searched the internet but I haven't found any suitable code.
You have plenty ways and tools to achieve what you want. I put one of them here:
For this one I use angular-file-upload as client side. So you need this one in your controller:
$scope.onFileSelect = function(image) {
if (angular.isArray(image)) {
image = image[0];
}
// This is how I handle file types in client side
if (image.type !== 'image/png' && image.type !== 'image/jpeg') {
alert('Only PNG and JPEG are accepted.');
return;
}
$scope.uploadInProgress = true;
$scope.uploadProgress = 0;
$scope.upload = $upload.upload({
url: '/upload/image',
method: 'POST',
file: image
}).progress(function(event) {
$scope.uploadProgress = Math.floor(event.loaded / event.total);
$scope.$apply();
}).success(function(data, status, headers, config) {
$scope.uploadInProgress = false;
// If you need uploaded file immediately
$scope.uploadedImage = JSON.parse(data);
}).error(function(err) {
$scope.uploadInProgress = false;
console.log('Error uploading file: ' + err.message || err);
});
};
And following code in your view (I also added file type handler for modern browsers):
Upload image <input type="file" data-ng-file-select="onFileSelect($files)" accept="image/png, image/jpeg">
<span data-ng-if="uploadInProgress">Upload progress: {{ uploadProgress }}</span>
<img data-ng-src="uploadedImage" data-ng-if="uploadedImage">
For server side, I used node-multiparty.
And this is what you need in your server side route:
app.route('/upload/image')
.post(upload.postImage);
And in server side controller:
var uuid = require('node-uuid'),
multiparty = require('multiparty'),
fs = require('fs');
exports.postImage = function(req, res) {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
var file = files.file[0];
var contentType = file.headers['content-type'];
var tmpPath = file.path;
var extIndex = tmpPath.lastIndexOf('.');
var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
// uuid is for generating unique filenames.
var fileName = uuid.v4() + extension;
var destPath = 'path/to/where/you/want/to/store/your/files/' + fileName;
// Server side file type checker.
if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
fs.unlink(tmpPath);
return res.status(400).send('Unsupported file type.');
}
fs.rename(tmpPath, destPath, function(err) {
if (err) {
return res.status(400).send('Image is not saved:');
}
return res.json(destPath);
});
});
};
As you can see, I store uploaded files in file system, so I just used node-uuid to give them unique name. If you want to store your files directly in database, you don't need uuid, and in that case, just use Buffer data type.
Also please take care of things like adding angularFileUpload to your angular module dependencies.
I got ENOENT and EXDEV errors. After solving these, below code worked for me.
var uuid = require('node-uuid'),
multiparty = require('multiparty'),
fs = require('fs');
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
var file = files.file[0];
var contentType = file.headers['content-type'];
var tmpPath = file.path;
var extIndex = tmpPath.lastIndexOf('.');
var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
// uuid is for generating unique filenames.
var fileName = uuid.v4() + extension;
var destPath = appRoot +'/../public/images/profile_images/' + fileName;
// Server side file type checker.
if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
fs.unlink(tmpPath);
return res.status(400).send('Unsupported file type.');
}
var is = fs.createReadStream(tmpPath);
var os = fs.createWriteStream(destPath);
if(is.pipe(os)) {
fs.unlink(tmpPath, function (err) { //To unlink the file from temp path after copy
if (err) {
console.log(err);
}
});
return res.json(destPath);
}else
return res.json('File not uploaded');
});
for variable 'appRoot' do below in express.js
path = require('path');
global.appRoot = path.resolve(__dirname);

Resources