Output multiple files with GraphicsMagick - javascript

I have the following function set up which captures uploaded images, converts them and them writes them to the server. How do I go about renaming the converted file?
Currently the function takes the uploaded image and saves out one version of the image.
Goals:
Save out multiple versions of the uploaded image depending on image size.
Append filename with image size, eg. filename-400x400.jpg, filename-200x200.jpg
I'm not too sure how to go about the first one and the second, I think busboy is interfering with it, as filename in the function below includes to the extension. I need to be able to append the filename with the size before the extension. Can this be done with a regex?
Function:
// upload new user image
app.post('/api/users/user/upload_image', function (req, res) {
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
console.log('\n\nUploading file: '.underline +filename .underline);
console.log('Uploaded\nConverting...');
var size = { width: 200, height: 200 };
gm(file,'www/uploads/' + filename)
.resize(size.width * 2, (size.height * 2) + '')
.thumbnail(size.width, size.height + '^')
.gravity('Center')
.extent(size.width, size.height)
.file(filename)
.profile('*')
.write('www/uploads/' + filename, function (err) {
console.log("Success!".bold);
});
});
req.busboy.on('finish', function () {
res.writeHead(303, { Connection: 'close', Location: '/' });
res.end();
});
});

As far as creating multiple files goes, you can just duplicate the gm chain you have already, just use a different filename.
For the filename problem you could either just create your own filenames or use a regexp to insert the dimensions into the original filename. The latter you could use something like:
// changes "foo.jpg" to "foo-400x400.jpg"
filename.replace(/\.[^\.]+$/, '-400x400$&')

Related

Multiple File Download in Javascript Requires Timeout

I'd like to download multiple files at once within a browser where the file contents already exist in memory.
There are already resources that show how to do this: see this article and the downloadjs library.
I'm able to download a single file, but not multiple when the download calls are near each other. I've attached a sample that shows three consecutive download calls where only the final file is downloaded. Below that is code that spaces the calls apart by 100ms, which does download all the files.
Why is this the case?
Thus far, I've only tested the code in Chrome.
Download function:
function download(contents, filename, filetype) {
// From: https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server
var element = document.createElement('a');
element.setAttribute('href', 'data:' + filetype + ',' + encodeURIComponent(contents));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
Example usage:
<button onclick="startDownload()">Click to download</button>
<script>
function startDownload() {
download("hello 0", "hello0.txt", "text/plain");
download("hello 1", "hello1.txt", "text/plain");
// hello2.txt is the only one that downloads
download("hello 2", "hello2.txt", "text/plain");
}
</script>
What does work is spacing the download calls apart with a setTimeout() call.
E.g.
function downloadWrapper(contents, filename, filetype, timeoutMultiplier) {
setTimeout(
function() {
download(contents, filename, filetype);
}, timeoutMultiplier * 100);
}
// All of these download; they're spaced apart by 100 ms
downloadWrapper("hello 0", "hello0.txt", "text/plain", 0);
downloadWrapper("hello 1", "hello1.txt", "text/plain", 1);
downloadWrapper("hello 2", "hello2.txt", "text/plain", 2);
The above workaround works, though I'm not sure why. Any ideas would be much appreciated.

Cropping a profile picture in Node JS

I want the user on my site to be able to crop an image that they will use as a profile picture. I then want to store this image in an uploads folder on my server.
I've done this using php and the JCrop plugin, but I've recently started to change the framework of my site to use Node JS.
This is how I allowed the user to crop an image before using JCrop:
$("#previewSub").Jcrop({
onChange: showPreview,
onSelect: showPreview,
aspectRatio: 1,
setSelect: [0,imgwidth+180,0,0],
minSize: [90,90],
addClass: 'jcrop-light'
}, function () {
JcropAPI = this;
});
and I would use php to store it in a folder:
<?php
$targ_w = $targ_h = 300;
$jpeg_quality = 90;
$img_r = imagecreatefromjpeg($_FILES['afile']['tmp_name']);
$dst_r = ImageCreateTrueColor( $targ_w, $targ_h );
imagecopyresampled($dst_r,$img_r,0,0,$_POST['x'],$_POST['y'],
$targ_w,$targ_h,$_POST['w'],$_POST['h']);
header("Content-type: image/jpeg");
imagejpeg($dst_r,'uploads/sample3.jpg', $jpeg_quality);
?>
Is there an equivalent plugin to JCrop, as shown above, using Node JS? There are probably multiple ones, if there are, what ones would you recommend? Any simple examples are appreciated too.
EDIT
Because the question is not getting any answers, perhaps it is possible to to keep the JCrop code above, and maybe change my php code to Node JS. If this is possible, could someone show me how to translate my php code, what would would be the equivalent to php above?
I am very new to Node, so I'm having a difficult time finding the equivalent functions and what not.
You could send the raw image (original user input file) and the cropping paramenters result of JCrop.
Send the image enconded in base64 (string), when received by the server store it as a Buffer :
var img = new Buffer(img_string, 'base64');
ImageMagick Doc : http://www.imagemagick.org/Usage/files/#inline
(inline : working with base64)
Then the server has the image in a buffer and the cropping parameters.
From there you could use something like :
https://github.com/aheckmann/gm ...or...
https://github.com/rsms/node-imagemagick
to cast the modifications to the buffer image and then store the result in the file system.
You have other options, like manipulating it client-side and sending the encoded image result of the cropping.
EDIT : First try to read & encode the image when the user uses the input :
$('body').on("change", "input#selectImage", function(){readImage(this);});
function readImage(input) {
if ( input.files && input.files[0] ) {
var FR = new FileReader();
FR.onload = function(e) {
console.log(e.target.result);
// Display in <img> using the b64 string as src
$('#uploadPreview').attr( "src", e.target.result );
// Send the encoded image to the server
socket.emit('upload_pic', e.target.result);
};
FR.readAsDataURL( input.files[0] );
}
}
Then when received at the server use the Buffer as mentioned above
var matches = img.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/), response = {};
if (matches.length !== 3) {/*invalid string!*/}
else{
var filename = 'filename';
var file_ext = '.png';
response.type = matches[1];
response.data = new Buffer(matches[2], 'base64');
var saveFileAs = 'storage-directory/'+ filename + file_ext;
fs.unlink(saveFileAs, function() {
fs.writeFile(saveFileAs, response.data, function(err) {if(err) { /* error saving image */}});
});
}
I would personally send the encoded image once it has been edited client-side.
The server simply validates and saves the file, let the client do the extra work.
As promised, here is how I used darkroom.js with one of my Express projects.
//Location to store the image
var multerUploads = multer({ dest: './uploads/' });
I upload the image first, and then allow the user to crop it. This is because, I would like to keep the original image hence, the upload jade below:
form(method='post', action='/user/image/submit', enctype='multipart/form-data')
input(type='hidden', name='refNumber', value='#{refNumber}')
input(type='file', name='photograph' accept='image/jpeg,image/png')
br
input(type='submit', value='Upload image', data-role='button')
Here is the form I use to crop the image
//- figure needed for darkroom.js
figure(class='image-container', id='imageContainer')
//specify source from where it should load the uploaded image
img(class='targetImg img-responsive', src='/user/image/view/#{photoName}', id='target')
form(name='croppedImageForm', method='post', enctype='multipart/form-data', id='croppedImageForm')
input(type='hidden', name='refNumber', id='refNumber', value='#{refNumber}')
input(type='hidden', id='encodedImageValue', name='croppedImage')
br
input(type='submit', value='Upload Cropped Image', id='submitCroppedImage' data-role='button')
The darkroom.js is attached to the figure element using this piece of javascript.
new Darkroom('#target', {
// Canvas initialization size
minWidth: 160,
minHeight: 160,
maxWidth: 900,
maxHeight: 900,
});
});
Once you follow the STEP 1, STEP 2 and finally STEP 3 the base64 value of the cropped region is stored in under figure element see the console log shown in the screenshot below:
I then have a piece of javascript that is triggered when the Upload Cropped Image is clicked and it then copy/paste the base64 value of the img from figure into the input element with id encodedImageValue and it then submit it to the server. The javascript function is as follow:
$("#submitCroppedImage").click(function() {
var img = $('#imageContainer img');
var imgSrc = img.attr('src');
if(imgSrc !== undefined && imgSrc.indexOf('base64') > 0) {
$('#encodedImageValue').val(img.attr('src'));
$.ajax({
type: "POST",
url: "/user/image/cropped/submit",
data: $('#croppedImageForm').serialize(),
success: function(res, status, xhr) {
alert('The CROPPED image is UPLOADED.');
},
error: function(xhr, err) {
console.log('There was some error.');
}
});
} else {
alert('Please follow the steps correctly.');
}
});
Here is a screenshot of POST request with base64 field as its body
The post request is mapped to the following route handler in Express app:
router.post('/user/image/cropped/submit',
multerUploads,
function(req, res) {
var photoName = null;
var refNumber = req.body.refNumber;
var base64Data = req.body.croppedImage.replace(/^data:image\/png;base64,/, "");
fs.writeFile("./uploads/cropped-" + 'profile_image.png', base64Data, 'base64',
function(err) {
logger.info ("Saving image to disk ...");
res.status(200).send();
});
});
I have the following .js files relating to awesome Fabric.js and darkroom.js
script(src='/static/js/jquery.min.js')
script(src='/static/js/bootstrap.min.js')
//get the js files from darkroom.js github
script(src='/static/js/fabric.js')
script(src='/static/js/darkroom.js')
script(src='/static/js/plugins/darkroom.crop.js')
script(src='/static/js/plugins/darkroom.history.js')
script(src='/static/js/plugins/darkroom.save.js')
link(href='/static/css/darkroom.min.css', rel='stylesheet')
link(href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.css', rel='stylesheet')
//get this from darkroom.js github
link(href='/static/css/page.css', rel='stylesheet')
Lastly, also copy the svg icons for selecting, cropping, saving, etc (from darkroo.js github page).

PhantomJS - Capture screen to HTMLImageElement, HTMLCanvasElement or ImageData

Is it possible with PhantomJS to capture a web page screen without saving it to the file system?
I need this because I want to make some post processing (color scheme detection) with the page screenshot.
The color detection is also made with JS .(https://github.com/leeoniya/RgbQuant.js) and can handle HTMLImageElement, HTMLCanvasElement, ImageData or CanvasRenderingContext2D
Here is the PhantomJS API, but the render method only supports a file path: http://phantomjs.org/screen-capture.html
Thank you Artjom B. for your solution in the comment. It worked fine.
First I created the screenshot of the page, and saved it to the folder screenshots with an html file that includes this screenshot.
page.open(url, function (status) {
// Make screenshot and save it to './screenshots'
var rel_path = 'screenshots/' + btoa(url) + '_' + (new Date().getTime());
// Image path
var img_path = rel_path + '.png';
// HTML page path
var html_path = rel_path + '.html';
// Make screenshot
page.render(img_path);
// Create webpage with the image, but take the absolute path.
fs.write(html_path, '<html><body><img id="image" src="' + 'file:///' + fs.absolute(img_path) + '"/></body></html>', 'w');
});
After that I opend the created file again with phantom. Loaded the image by the id=image and performed my RGB-Quantification.
p.open('file:///' + html_path, function start(status) {
console.log('[RGB-QUANTIFICATION]: ' + self.url);
// Inject image quantification library
p.injectJs('ext/rgbquant.js');
// Generate color palette
var colors = p.evaluate(function () {
var rgb_quant_settings = {colors: 5, method: 2, initColors: 4096, minHueCols: 0};
var rgb_quant = new RgbQuant(rgb_quant_settings);
rgb_quant.sample(document.getElementById('image'));
return rgb_quant.palette(true);
});
...
});

How to pick(choose) multiple images using camera API at the same time in phonegap?

How to choose or pick multiple images at the same time in phonegap camera API when using Camera.DestinationType.FILE_URI. I am able to pick only one images at a time. I am able to pick multiple files(including txt,pdf..) in sdcard using this. So i want same like for images.
navigator.camera.getPicture(function(imageData) {
window.resolveLocalFileSystemURI(imageData, function(fileEntry) {
fileEntry.file(function(fileObj) {
}, onFail, {
quality : 50,
destinationType : Camera.DestinationType.FILE_URI
});
My cordova version 3.3, Jquery Mobile 1.3.2.
Please suggest any plugins are available to do this.
Use this Cordova multiple image selector plugin to choose multiple image at a time. It is good plugin for choose multiple images.
Download the above plugin and copy paste the java classes. Set the required permission. Don't forget to copy the res folder just copy and paste in your res folder.
Inside assets/www create imagepicker.js copy and paste the dowloaded imagepicker.js
In your index.html set like this:
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="imagepicker.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready",onDeviceReady,false);
function onDeviceReady(){
window.imagePicker.getPictures(
function(results) {
for (var i = 0; i < results.length; i++) {
alert('Image URI: ' + results[i]);
// read file type and size and file name like below(in comment)
/* window.resolveLocalFileSystemURI(results[i], function(fileEntry){
fileEntry.file(function(fileObj) {
alert(fileEntry.name);
alert(fileObj.size);
alert(fileObj.type);
});
}, function (error) {
alert('Error: ' + error);
});*/
}
}, function (error) {
alert('Error: ' + error);
}
);
}
</script>
Note: This should work only cordova 3.0 and above and android 4.0 and above
Open CameraLauncher.java file and replace these lines
String resizePath = getTempDirectoryPath() + "/resize.jpg";
this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis());
to
String resizePath = getTempDirectoryPath() + "/resize"+System.currentTimeMillis()+".jpg";
this.callbackContext.success("file://" + resizePath);
var x=0;
function onPhotoDataSuccess(imageURI)
{
x++;
// Uncomment to view the base64-encoded image data
console.log(imageURI);
alert(imageURI);
// Get image handle
//
var y = 'smallImage'+x;
var smallImage = document.getElementById(y);
alert(smallImage);
smallImage.src = "data:image/jpeg;base64," + imageURI;
// Unhide image elements
//
smallImage.style.display = 'block';
// Show the captured photo
// The in-line CSS rules are used to resize the image
//
//var fso=new ActiveXObject("Scripting.FileSystemObject");
//fso.CopyFile("data:image/jpeg;base64," + imageURI,"file:///storage/sdcard/DCIM/");
alert(smallImage.src)
}
where x is the loop for doing the multiple image from camera as well as from photogallery

Updating an image that is re-uploaded periodically

I have a webcam script that sends a JPG via FTP to my webserver every 10 seconds (overwriting the original).
How can I get jQuery to refresh that image? I tried:
window.onload = function() {
$('body').prepend('<img id="cam" src="ww.jpg" alt="" />');
setInterval(runAgain, 12500);
};
function runAgain() {
$.ajax({
url:'ww.jpg',
cache:false,
beforeSend:function() {
$('#cam').remove();
},
success:function() {
$('body').prepend('<img id="cam" src="ww.jpg" alt="" />');
}
});
}
Note: I don't want to refresh the page if I can help it.
A dirty way is appending a timestamp or a random number at the end of the image src, to prevent the caching, like
img src="image.jpg?random=[RANDOM]"
where [RANDOM] is the timestamp or the random number
It looks like you have the problem with the caching - browser already has the image in the cache and doesn't load it again from server. The simplest way is to add the random parameter that will make the browser think that it is the other image:
...
url:'ww.jpg?' + Math.random()
...
It is possible to achieve the same effect with serverside tune-ups, but this way is probably least intrusive and easier to implement.
You actually only need to refresh the src. Of course you should use a cache buster to avoid browser caching the image destonation. A solution in this case would be random query string parameter.
$(function() {
var $img = $('<img>', {
src: 'ww.jpg'
}).appendTo(document.body);
setInterval(function() {
$img.attr('src', function(_, src) {
return [src, '?', ~~(Math.random() * 40000)].join('');
});
}, 12500);
});
function updateLogo() {
var base_src = "http://www.google.com/logos/2011/graham11-hp-start.png";
var logo = $('#logo');
var timestamp = new Date().getTime();
// uncomment the timestamp part if your server supports an extra parameter to prevent caching
logo.attr('src', base_src);// + '?_ts=' + timestamp);
};
setInterval(updateLogo, 1000);
If your server supports an extra parameter in the image URL you'll prevent caching the image.

Categories

Resources