I'm trying to get the original dimensions for an image while uploading it to a database. Actually it would be great to get all of it's original metadata (XMP, Adobe). But even getting the dimensions is not working:
Template.pixUpload.events({
'change .myPixInput': function(event, template) {
FS.Utility.eachFile(event, function(file) {
// get the image's width
var img = event.target.files[0]
var imgwidth = img.width;
console.log('width: ' + width);
var newFile = new FS.File(file);
newFile.metadata = {width: imgwidth};
MyPix.insert(newFile, function (err, fileObj) {
//If !err, we have inserted new doc with ID fileObj._id, and
//kicked off the data upload using HTTP
});
});
}
});
I use Imagemagick to get all kinds of metadata (like EXIF) from my images.
var assetStore = new FS.Store.GridFS("assetFiles", {
transformWrite: function(fileObj, readStream, writeStream) {
readStream.pipe(writeStream);
// write the image data to the fileobj
getBinaryData(readStream, FS.Utility.safeCallback(function(err, binary) {
var imageData = Imagemagick.identify({
data: binary
});
fileObj.update({
$push: {
data: imageData
}
});
}));
}
});
getBinaryData is a async function that returns the binary data of my image.
I use a package called classcraft:imagemagick since the graphicsmagick package does not give you as much metadata as imagemagick
This works! – copy/modified from a discussion with Sanjo at GitHub. Only problem is I don't fully understand what's happening. Can anyone help me out?
var OriginalsStore = new FS.Store.FileSystem("OriginalPix", {
path: pathToOriginalsFolder,
transformWrite: function (fileObj, readStream, writeStream) {
// write original image to writeStream, no transformations
readStream.pipe(writeStream);
gm(readStream, fileObj.name())
.size({bufferStream: true}, FS.Utility.safeCallback(function (err, size) {
if (err) {
// handle the error
} else {
fileObj.update({$set: {'metadata.width': size.width, 'metadata.height': size.height}});
}
}));
}
});
This is what are you looking for?
A normal image don't come with the 'width; field, it comes with type,name,dateMod,dateUp,and size.(well atleast my files)
Template.pixUpload.events({
'change .myPixInput': function(event, template) {
FS.Utility.eachFile(event, function(file) {
// get the image's width
var img = event.target.files[0]
var imgwidth = img.width;
console.log('width: ' + width);
console.log(img.lastModified);
console.log(img.lastModifiedDate);
console.log(img.name);
console.log(img.size);
console.log(img.type);
var newFile = new FS.File(file);
newFile.metadata = {
width: imgwidth
name:img.name,
size:img.size,
type:img.type,
lstModDate:img.lastModifiedDate
lstDate:img.lastModified
};
MyPix.insert(newFile, function (err, fileObj) {
//If !err, we have inserted new doc with ID fileObj._id, and
//kicked off the data upload using HTTP
});
});
}
});
Test it
Related
Is there any way to generate a csv file and download it in the client side using meteor? I would the user to be able to click a button and then a file would be downloaded without leaving the current page.
Template.analytics.events({
'click .csv-export' (event, template) {
const json2csv = require('json2csv');
let fields = ['timestamp', 'user', 'category', 'action'];
let data = Analytics.find({}).fetch();
try {
let result = json2csv({data: data, fields: fields});
var filename = 'test.csv';
var headers = {
'Content-Type': 'text/csv',
'Content-Disposition': "attachment; filename=" + filename
};
this.response.writeHead(200, headers);
this.response.end(result);
} catch (err) {
console.error(err);
}
}
});
Thanks to #Jankapunkt I was able to solve this.
Here's the answer, all the code is inside Template.onRendered.
// DOWNLOAD CSV
this.csvFile = null,
makeTextFile = function() {
var csv = json2csv(Analytics.find({}).fetch(), true, false);
var data = new Blob([csv], {
type: 'text/csv'
});
if (this.csvFile !== null) {
window.URL.revokeObjectURL(this.csvFile);
}
this.csvFile = window.URL.createObjectURL(data);
return this.csvFile;
};
let csvExport = document.getElementById('csv-export');
csvExport.addEventListener('click', function() {
let downloadLink = makeTextFile();
window.open(downloadLink, '_blank');
}, false);
//END DOWNLOAD CSV
I cannot seem to find any documentation that will explain how I can get the filename and filepath of an uploaded collectionFS image into my meteor method.
I am able to get the image URL on the client side no problem using helpers, but I cannot seem to figure out how I can send the filename and filepath of the attached image to my method.
Method JS
Meteor.methods({
addQuote: function(data) {
check(data, Object);
var attachments = [];
var html = html;
// need to get the filename and filepath from collectionFS
// I would then have the data go here
attachments.push({filename: , filePath: });
this.unblock();
var email = {
from: data.contactEmail,
to: Meteor.settings.contactForm.emailTo,
subject: Meteor.settings.contactForm.quoteSubject,
html: html,
attachmentOptions: attachments
};
EmailAtt.send(email);
}
});
Controller JS
function ($scope, $reactive, $meteor) {
$reactive(this).attach($scope);
this.user = {};
this.helpers({
images: () => {
return Images.find({});
}
});
this.subscribe('images');
this.addNewSubscriber = function() {
// Uploads the Image to Collection
if(File.length > 0) {
Images.insert(this.user.contactAttachment);
console.log(this.user.contactAttachment);
}
// This is the variable I use to push to my method
// I image I need to push the filename and filepath also
// I am unsure how to access that information in the controller.
var data = ({
contactEmail: this.user.contactEmail,
contactName: this.user.contactName,
contactPhone: this.user.contactPhone,
contactMessage: this.user.contactMessage
});
// This will push the data to my meteor method "addQuote"
$meteor.call('addQuote', data).then(
function(data){
// Show Success
},
function(err) {
// Show Error
}
);
};
You can use the insert callback to get this informations:
Images.insert(fsFile, function (error, fileObj)
{
if (error) console.log(error);
else
{
console.log(fileObj);
//Use fileObj.url({brokenIsFine: true}); to get the url
}
});
I need to upload 'on fly' user's image. But i get error 503 Service Unavailable.
user.js
Meteor.subscribe('userImages');
Template.userProfil.events({
'change [name=userPhotoUpload]': function(event) {
event.preventDefault();
FS.Utility.eachFile(event, function(file) {
var newFile = new FS.File(file);
newFile.metadata = {
createdBy:Meteor.userId(),
}
userImages.insert(newFile, function (err, fileObj) {
if (err){
// handle error
} else {
// handle success depending what you need to do
var currentUserId = Meteor.userId();
var imagesURL = {
"profile.userImg": '/cfs/files/userImages/' + fileObj._id + '/' + fileObj.name()
};
Meteor.users.update(currentUserId, {$set: imagesURL});//there I get url and
}
});
});
}
});
router.js
Router.route('/organizer', {
name: 'userProfil',
template: 'userProfil',
data: function() {
var currentUser = Meteor.userId();
return Meteor.user({_id: currentUser});
}
});
user-img.html
<img src="{{profile.userImg}}">
after uploding image i get this err:
http://localhost:3000/cfs/files/userImages/wNjvF8uuN8j6fd8md/exampl2.jpg 503 (Service Unavailable)
But this path is absolutely correct, and after manual reloading page it's work.
How can I solve this problem?
Ok, I found some solution, but I don't think that it's correct way. Maybe someone have better decision?
Changing:
user.js
'change [name=userPhotoUpload]': function(event) {
event.preventDefault();
FS.Utility.eachFile(event, function(file) {
var newFile = new FS.File(file);
newFile.metadata = {
createdBy:Meteor.userId(),
}
userImages.insert(newFile, function (err, fileObj) {
if (err){
// handle error
} else {
var currentUserId = Meteor.userId();
var intervalHandle = Meteor.setInterval(function () {
console.log("Inside interval");
// changes here:
if (fileObj.hasStored("userImages")) {
//checked if image was stored
var imagesURL = {
"profile.userImg": '/cfs/files/userImages/' + fileObj._id + '/' + fileObj.name()
};
Meteor.users.update(currentUserId, {$set: imagesURL});
// if file has stored, stop interval
Meteor.clearInterval(intervalHandle);
}
}, 1000);
}
});
});
}
My question is solved:
It missed this line of code before the insert code:
var fileObj = new FS.File(file);
How to access FS collections metadata in meteor?
I tried: var result = Images.find({"metadata.gallery._id":curgallery})
(curgallery is the _id of a gallery object)
Here is my code:
helper:
images: function () {
if (Session.get('gallery')) {
var curgallery = Session.get('gallery')._id;
var result = Images.find({"metadata.gallery._id":curgallery})
console.log(result.fetch());//return an empty array
return result;
};
events:
//INSERT IMAGE
'change .fileInput': function(event, template) {
if (Session.get('gallery')) {
var collection = Session.get('gallery');
FS.Utility.eachFile(event, function(file) {
file.metadata = {owner: Meteor.user()._id,gallery:collection};
Images.insert(file, function (err, fileObj) { } );
console.log(file.metadata.gallery._id);//return the _id's gallery
});
};
},
Is there any best way to resize and save files to s3 in meteor.
I though about using cfs packages, but they put too much load on server.
To directly upload images to the s3 I'm using slingshot which is very fine.
but slingshot takes only file objects as inputs it doesn't take streams to store the files.
Is there any option to resize the image in client side and pass it to the slingshot package
package: https://github.com/CulturalMe/meteor-slingshot
issue: https://github.com/CulturalMe/meteor-slingshot/issues/36
Yes it's possible with clientside-image-manipulation, here's my untested interpretation of the docs:
Template.upload.events({
'change #image-upload': function(event, target) {
var uploader = new Slingshot.Upload("myFileUploads");
var file = event.target.files[0];
var img = null;
processImage(file, 300, 300, function(data){
uploader.send(data, function (error, downloadUrl) {
if(error)
throw new Meteor.Error('upload', error);
Meteor.users.update(Meteor.userId(), {$push: {"profile.files": downloadUrl}});
});
});
}
});
There are other image related plugins worth investigating at atmosphere.js
I've found that the following works in order to integrate Clientside Image Manipulation with Slingshot (asynchronous code with ES6 promises):
var uploader;
function b64ToBlob(b64Data, contentType, sliceSize) {
var byteNumbers, i, slice;
var offset = 0;
var byteCharacters = atob(b64Data);
var byteArrays = [];
sliceSize = sliceSize || 512;
byteArrays = [];
while (offset < byteCharacters.length) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = [];
for (i = 0; i < slice.length; ++i) {
byteNumbers.push(slice.charCodeAt(i));
}
byteArrays.push(new Uint8Array(byteNumbers));
offset += sliceSize;
}
return new Blob(byteArrays, {type: contentType});
}
uploader = new Slingshot.Upload("pictures");
new Promise(function (resolve) {
processImage(file, 300, 300, resolve);
}).then(function (dataUri) {
var match = /^data:([^;]+);base64,(.+)$/.exec(dataUri);
return [file.name, match[1], match[2]];
}).then(function (params) {
var name = params[0];
var type = params[1];
var b64 = params[2];
return new Promise(function (resolve, reject) {
var blob = b64ToBlob(b64, type);
blob.name = name;
uploader.send(blob, function (error, downloadUrl) {
if (error != null) {
reject(error.message);
} else {
resolve(downloadUrl);
}
});
});
});
Conversion from Base64 to blob is borrowed from https://stackoverflow.com/a/16245768/1238764.
There are a lot of image manipulation plugins. The one I use is: cropper
It's a jquery based plugin which basically does the thing you want + a bit more. After manipulation you can convert the image to a canvas and with the dataToUrl() method you can pass the data of the new manipulated image to any datasource of your choice.