Getting image file size transferred with socket.io - javascript

in a Node.js with Socket.io project, i get an image via Socket.io like this:
socket.on('newImage', function (data) {
var desc = data.description;
var image = data.img; //i want file size of this
}
in this code, image variable contains image binary data, i want to detect file size of that image. how?

Depending on how the data is encoded you can retrieve the byte length by using the byteLength method:
var encoding = 'binary';
var data = new Buffer('hello world', encoding);
Buffer.byteLength(data, encoding); // 11

Related

File Uploading ReadAsDataUrl

I have a question about the File API and uploading files in JavaScript and how I should do this.
I have already utilized a file uploader that was quite simple, it simply took the files from an input and made a request to the server, the server then handled the files and uploaded a copy file on the server in an uploads directory.
However, I am trying to give people to option to preview a file before uploading it. So I took advantage of the File API, specifically the new FileReader() and the following readAsDataURL().
The file object has a list of properties such as .size and .lastModifiedDate and I added the readAsDataURL() output to my file object as a property for easy access in my Angular ng-repeat().
My question is, it occurred to me as I was doing this that I could store the dataurl in a database rather than upload the actual file? I was unsure if modifying the File data directly with it's dataurl as a property would affect its transfer.
What is the best practice? Is it better to upload a file or can you just store the dataurl and then output that, since that is essentially the file itself? Should I not modify the file object directly?
Thank you.
Edit: I should also note that this is a project for a customer that wants it to be hard for users to simply take uploaded content from the application and save it and then redistribute it. Would saving the files are urls in a database mitigate against right-click-save-as behavior or not really?
There is more then one way to preview a file. first is dataURL with filereader as you mention. but there is also the URL.createObjectURL which is faster
Decoding and encoding to and from base64 will take longer, it needs more calculations, more cpu/memory then if it would be in binary format.
Which i can demonstrate below
var url = 'https://upload.wikimedia.org/wikipedia/commons/c/cc/ESC_large_ISS022_ISS022-E-11387-edit_01.JPG'
fetch(url).then(res => res.blob()).then(blob => {
// Simulates a file as if you where to upload it throght a file input and listen for on change
var files = [blob]
var img = new Image
var t = performance.now()
var fr = new FileReader
img.onload = () => {
// show it...
// $('body').append(img)
var ms = performance.now() - t
document.body.innerHTML = `it took ${ms.toFixed(0)}ms to load the image with FileReader<br>`
// Now create a Object url instead of using base64 that takes time to
// 1 encode blob to base64
// 2 decode it back again from base64 to binary
var t2 = performance.now()
var img2 = new Image
img2.onload = () => {
// show it...
// $('body').append(img)
var ms2 = performance.now() - t2
document.body.innerHTML += `it took ${ms2.toFixed(0)}ms to load the image with URL.createObjectURL<br><br>`
document.body.innerHTML += `URL.createObjectURL was ${(ms - ms2).toFixed(0)}ms faster`
}
img2.src = URL.createObjectURL(files[0])
}
fr.onload = () => (img.src = fr.result)
fr.readAsDataURL(files[0])
})
The base64 will be ~3x larger. For mobile devices I think you would want to save bandwidth and battery.
But then there is also the latency of doing a extra request but that's where http 2 comes to rescue

Unable to make an Image Blob JSON Serializable

JIRA Ticket created due to base64encode failure: https://jira.appcelerator.org/browse/TC-5876
My Current CFG:
Titanium SDK 5.1.2.GA
Testing on an iPhone iOS 9.1
I'm stuck in a problem in a project for a client that requires images took on device (using the camera) to be sent to a WebService and afterwards be seen on any device using the app (both Android and iOS devices).
Titanium provides a Ti.Blob object (event.media) after taking a picture (which is not JSON serializable) and I need somehow to send this to the server. The server responds always a JSON object, thus this Blob must be somehow JSON serializable.
I've tried many ways without success:
1 - Base64Encode the Blob
var base64blob = Ti.Utils.base64encode(event.media);
Doesn't work, it stucks the app and throws a ASL exceeded maximum size error. I imagine that the image is too large to be base64encoded.
2 - Read the Blob into a Buffer
var blobStream = Ti.Stream.createStream({ source: event.media, mode: Ti.Stream.MODE_READ });
var buffer = Ti.createBuffer({ length: event.media.length });
var bytes = blobStream.read(buffer);
It works but I have no idea how can I transform this buffer holding the image contents into something that the server can return in a JSON object and later be transformed into an Image Blob again.
The server can't manage Ti.Blob objects or Ti.Buffer objects because, first of all, they are Titanium objects and the server is C# based, and second due to Ti.Blob and Ti.Buffer aren't JSON serializable, thus the JSON return doesn't work.
What I need is basically described in the imaginary example below:
var imageBlob = event.media;
var JSONSerializableImg = imageBlob.toJSON();
sendImageToServer(JSONSerializableImg);
var imgFromServer = getImageFromServer();
var imageBlob = imgFromServer.toBlob();
var imgView = createImageView({
image: imageBlob
});
I hope someone can help me with any conversion method possible.
Thank's
OK,
This is what I think you have to do. Looking at the API, this is very doable.
1: You need to create an object Server side that will hold the BLOB.
public class BlobContainer
{
public string fileName{get;set;}
//... (Other properties)
public byte[] data {get;set;}
}
2: Convert the important information from the BLOB into a binary array and send to server.
var blobStream = Ti.Stream.createStream({ source: myBlob, mode: Ti.Stream.MODE_READ });
var newBuffer = Ti.createBuffer({ length: myBlob.length });
var bytes = blobStream.read(newBuffer);
3: Then send the byte data to the server through Ajax requests. Be mindful of how big your array is that you are sending. It might be advantageous to split the array up and combine it on the other side (Might not be necessary):
var dataObjects: [
{ id: 1, data: [BYTE_DATA_PART] },
{ id: 2, data: [BYTE_DATA_PART] }...
]
$.each(dataObjects, function(i,a) {
$.ajax({ url: "BLA", data: JSON.stringify(a), dataType: "json", type: "POST",
success: function() { //CONTINUE\\ },
error: function() {alert("ERROR BRO"})
});
});
4: Then server side get each request in your little blob container, store in a session object or cache object and once you have N out of N, piece it all together and store that sucker in the Database.
5: Retrieve the stuff in the reverse order. Just remember that it is stored as byte[] data. You may have to fuddle with it and store it as a string because of the way the TI buffer creates bytes and the way c# interprets bytes. Best thing is trial and error. Once you have all the pieces back on the client.
var newBuffer = Ti.Stream.read(data, 0, data.length);
var newBlob = newBuffer.toBlob();
To send and receive binary data to and from a server it's best to use Ti.Network.HTTPClient which can send and receive binary data.
There's a guide on uploading and downloading files here:
http://docs.appcelerator.com/platform/latest/#!/guide/File_Uploads_and_Downloads
JSON isn't designed to carry binary data, although base64encoded binary data should work. This is what Ti.Utils.base64encode() indeed is for. If you believe the "ASL exceeded maximum size error" you get shouldn't happen, please create a ticket on Appcelerator JIRA
I solved this issue by creating a separate method on server-side specially to upload photos. I followed the link below for server side:
PHP code
In Titanium I had to set XHR's header like this:
this.xhr.setRequestHeader("ContentType", "image/png");
this.xhr.setRequestHeader('enctype', 'multipart/form-data');
That's it!
Thank's for all the answers.
I've used the following method before (not in Titanium, but another web-based mobile app platform).
function convertToDataURLviaCanvas(url, callback, outputFormat){
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL(outputFormat);
callback(dataURL);
canvas = null;
};
img.src = url;
}
convertToDataURLviaCanvas('http://example.com/image.png', function(base64Img){
// Base64DataURL
});
I used this to send the base64 encoded image string as JSON to my backend server. Then re-encoded the image on the server. It worked for me, but the base64 encoded string is huge.

image onload not firing javascript when source is base64 websocket data

I have a Custom helpdesk application that I am trying to run in javascript.
the program has a peice of code that runs on the customer's computer and sends an image to a autobahn websocket server which acts as a proxy to a image viewer which can send clicks and keystrokes back over the websockets. this is all currently working in python correctly however, when i try it in javascript, I cannot get my image loaded no matter what I do and I can't figure out what i am doing wrong.
This is the part of my javascript that is broken:
function onMessage(evt) {
if(evt.data.indexOf('[00000]')>=0){
var ar = evt.data.split('[00000]');
if (ar[0] == "[IMAGE]"){
var imgdata = evt.data.split('[22222]');
context.width = imgdata[0];
context.height = imgdata[1];
console.log(context.width + " " + context.height)
try{
var img = new Image();
img.src = imgdata[2];
context.drawImage(img,0,0);
console.log("IMAGE");
}catch(e){
console.log(e)
}
}else if(ar[0] == "[RETCONN]"){
console.log("Accepted!");
}
}
}
Below is some code from the server to try and clarify what is going on with the javascript.
def onMessage(self, payload, isBinary):
if isBinary:
print("Binary message received: {0} bytes".format(len(payload)))
else:
msg = payload.decode('utf8')
com, data, arg = msg.split('[11111]')
if com == ("[IMAGE]"):
for name, conn in clients.items():
if name == data:
conn.sendMessage(('[IMAGE]'+'[00000]'+arg).encode('utf8'))
break
and this is how the image is put together from the customer side:
data = image.tostring()
data = base64.b64encode(data)
self.sendMessage(('[IMAGE]' + '[11111]' + rid + '[11111]' + str(w) + '[22222]' + str(h) + '[22222]' + data).encode('utf8'))
Anyone have any ideas how what i am doing wrong in my javascript?
EDIT: i am aware that img.src is in the wrong location. moving to the right spot does not fix the issue
If you want to use base64 encoded image as a source for the image object, it needs to be in format:
data:image/png;base64,<base64 encoded image>
You need to prepend data:image/png;base64, to your base64 string
img.src = "data:image/png;base64,"+imgdata[2];
You are also parsing your messages wrong.
imgdata[0] is supposed to be your width, but it also includes [IMAGE][00000].
You probably want ar[1].split('[22222]'); instead of evt.data.split('[22222]');.
To avoid complications with encoding, I would recommend you to use JSON to encode your WebSocket messages.
Edit:
You are getting a broken image because image.tostring() returns raw image data, but with data:image/png;base64, it's expected to be in PNG format.
To get base64 encoded PNG data use this:
import io
buffer = io.BytesIO()
image.save(buffer, "png")
data = base64.b64encode(buffer.getvalue())

Oracle Apex Canvas to blob in PNG format saved to item

I am struggling to convert my canvas URL to a blob format, specifically in PNG format. The DataURL is generating quite perfectly when I tested it without the conversion code, the problem comes in when trying to convert it to Blob. I am using an html button that triggers the JavaScript function with the ID buttonTextArea.
I need the Blob file to be linked to my APEX page item, which I use the $s('P9_IMAGE_CODE', Blob) format, which also works when I tested it using text only. Once the page is processed, that item will be sent into a blob column within my table.
Here is my code:
$("#buttonTextArea").click(function(dataURL) {
var BASE64_MARKER = ';base64,';
var canvas = document.getElementById('myCanvas');
var dataURL = canvas.toDataURL();
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = decodeURIComponent(parts[1]);
return new Blob([raw], {type: contentType});
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
document.getElementById("textArea").value = dataUrl;
$s('P9_IMAGE_CODE', Blob); --P9_IMAGE_CODE IS THE ITEM ON MY PAGE
});
I have been trying examples from :
https://github.com/blueimp/JavaScript-Canvas-to-Blob/blob/master/README.md
and
https://code.google.com/p/chromium/issues/detail?id=67587
Thank you in advance.
As Jeffrey pointed out, the javasscript "Blob" is not the same as the Oracle BLOB datatype.
The toDataURL function is already returning a base64 encoded string, so what you'd need to do is to save that string and then after submit convert it into a real blob.
Sending it to the database may be a bit of an issue since there is a limit of 32k bytes on an item's content. This means you'll likely have to send the string in chunks to the database. This is then not a VARCHAR2 (which also has a limit of 32k) but a CLOB.
As Wesley points out, there are some workarounds for that. The blog he linked to is one such example. There is also a plugin to facilitate the handling of clobs in apex.
Once you get the base64 string to the database, you'll have to convert it to a real blob. There is no built-in to quickly do this, but once again this is something several people have already solved.
A script is provided here by Tim Hall to convert a CLOB to a BLOB.
So:
In the browser, get the dataURL, which is a base64 encoded string
send it in chunks to the database, where it'll be a CLOB
after everything has been sent, convert the CLOB to a BLOB and save
it in your table

Sending an image and JSON data to server using Ajax POST request?

I want to send an image the user selected from their machine along with form data all wrapped up in a JSON object and sent to server. I am using Node for the server. Is it possible to place the image in the JSON object along with the other form elements and read in Node?
The common ways I encountered are using the Base64 string approach: you encode your image into a Base64 string and set it as part of the JSON Object that you send over to your server.
Another approach seems to be using the Binary Data in JSON but I'd never tried this before so not much info from me.
Here's a code sample to do a Base64 encoding in Javascript. Specifically look for the method below
function getBase64Image(imgElem) {
// imgElem must be on the same server otherwise a cross-origin error will be thrown "SECURITY_ERR: DOM Exception 18"
var canvas = document.createElement("canvas");
canvas.width = imgElem.clientWidth;
canvas.height = imgElem.clientHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(imgElem, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
There is a way to achieve this: use image data.
In Javascript, on client side, the FileReader will allow you to read binary image data, and get them into a base64 encoded string.
On client side:
var file = $('.file-upload > input')[0].files[0];
function upload(file) {
var reader = new FileReader();
// when image data was read
reader.onload = function(event) {
// I usually remove the prefix to only keep data, but it depends on your server
var data = event.target.result.replace("data:"+ file.type +";base64,", '');
// make here your ajax call
$.ajax({
url: 'and_so_on',
json: {
data: data
}
});
// read data from file
reader.readAsDataURL(file);
}
On server side, you will received base64 encoded image that you can easilly translate into binary with the Buffer constructor
var buffer = new Buffer(data, 'base64');
Be warned that FileReader is not supported by all browsers

Categories

Resources