I am playing with a simple exercise with WebSockets. I have a NodeJS script that listens on a WebSocket, receives a file name, opens the file, and sends the content of the file back to the client through the WebSocket.
Here is the code:
var websocket_server = require('ws').Server;
var server = new websocket_server({port: 1234});
var fs = require('fs');
server.on('connection', function connection(socket)
{
socket.on('message', function request(file_name)
{
fs.readFile(file_name, function(error, data)
{
if(error)
throw error;
socket.send(data, {binary: true});
});
});
});
On the client side, I use a WebSocket to load an image and display it in the page:
<!DOCTYPE html>
<html>
<head>
<title>My Little Project</title>
<script type = "text/javascript">
var ws = new WebSocket("ws://127.0.0.1:1234");
ws.binarytype = "blob";
ws.onmessage = function(event)
{
var img = document.createElement("img");
img.src = (window.URL || window.webkitURL).createObjectURL(event.data);
document.body.appendChild(img);
}
ws.onopen = function(event)
{
ws.send("image1.jpg")
}
</script>
</head>
<body>
</body>
</html>
Now, this example works fine, and I am happy. What makes me sad is that I would now like to ask the server for multiple images, and to do something with each.
I know that I could serialize all my requests by doing something like:
ws.onmessage = function(event)
{
// Do something with image1.jpg
ws.onmessage = function(event)
{
// Do something with image2.jpg
// And so on
}
ws.send("image2.jpg");
};
ws.send("image1.jpg");
But this is serial, which adds overhead and so on. What I would like to get is something like:
ws.send("image1.jpg", function(event)
{
// Do something with image1.jpg
});
ws.send("image2.jpg", function(event)
{
// Do something with image2.jpg
});
// And so on
I have no clue on how to do this neither on the client nor on the server, so any help would be greatly appreciated.
You should use the library websockets-callback which does exactly what you want.
Here is part of the code that is included in the demo:
function onTestProgressClick(){
var milliseconds = document.getElementById("callbackDelay").value;
//magic starts here
wsc.send({cmd: 'progress'},
function(response){
document.getElementById("ws_response").innerHTML = 'Progress test completed!';
},
function(response){
document.getElementById("progressBar").value = response.progress;
}
)
}
Why don't you just send images as json-encoded array and then handle it on server-side? I've not run this code, but it should work.
let images = [
"image1.jpg",
"image2.jpg",
];
ws.send(JSON.stringify(images), function(event)
{
// Do something with images
});
Related
I´m starting to customize/improve an old audio editor project. I can import audio tracks to my canvas VIA drag&drop from my computer. The thing is that I also would like to use audio tracks already stored in the server just clicking over a list of available tracks... instead of use the <input type="file"> tags. How can I read the server side files with a FileReader?Ajax perhaps? Thanks in advance.
This is the code for the file reader:
Player.prototype.loadFile = function(file, el) {
//console.log(file);
var reader = new FileReader,
fileTypes = ['audio/mpeg', 'audio/mp3', 'audio/wave', 'audio/wav'],
that = this;
if (fileTypes.indexOf(file.type) < 0) {
throw('Unsupported file format!');
}
reader.onloadend = function(e) {
if (e.target.readyState == FileReader.DONE) { // DONE == 2
$('.progress').children().width('100%');
var onsuccess = function(audioBuffer) {
$(el).trigger('Audiee:fileLoaded', [audioBuffer, file]);
},
onerror = function() {
// on error - show alert modal
var tpl = (_.template(AlertT))({
message: 'Error while loading the file ' + file.name + '.'
}),
$tpl = $(tpl);
$tpl.on('hide', function() { $tpl.remove() })
.modal(); // show the modal window
// hide the new track modal
$('#newTrackModal').modal('hide');
};
that.context.decodeAudioData(e.target.result, onsuccess, onerror);
}
};
// NOTE: Maybe move to different module...
reader.onprogress = function(e) {
if (e.lengthComputable) {
$progress = $('.progress', '#newTrackModal');
if ($progress.hasClass('hide'))
$progress.fadeIn('fast');
// show loading progress
var loaded = Math.floor(e.loaded / e.total * 100);
$progress.children().width(loaded + '%');
}
};
reader.readAsArrayBuffer(file);
};
return Player;
Thanks for the suggestion micronn, I managed to make a bypass without touch the original code. The code as follows is the following:
jQuery('.file_in_server').click(function()
{
var url=jQuery(this).attr('src');//Get the server path with the mp3/wav file
var filename = url.replace(/^.*[\\\/]/, '');
var path="http://localhost/test/audio/tracks/"+filename;
var file = new File([""], filename); //I need this hack because the original function recives a buffer as well as the file sent from the web form, so I need it to send at least the filename
var get_track = new XMLHttpRequest();
get_track.open('GET',path,true);
get_track.responseType="arraybuffer";
get_track.onload = function(e)
{
if (this.status == 200) //When OK
{
Audiee.Player.context.decodeAudioData(this.response,function(buffer){ //Process the audio toward a buffer
jQuery('#menu-view ul.nav').trigger('Audiee:fileLoaded', [buffer, file]); //Send the buffer & file hack to the loading function
},function(){
alert("Error opening file");
jQuery('#newTrackModal').modal('hide');
});
}
};
get_track.send();
});
After this, in the fileLoaded function, the track is added to the editor.
var name = 'Pista ' + Audiee.Collections.Tracks.getIndexCount();
track = new TrackM({buffer: audioBuffer, file: file, name: name}); //being audioBuffer my buffer, file the fake file and name the fake file name
Audiee.Collections.Tracks.add(track);
And... thats it!
I would like to seek your help about xmlhttprequest. I would like to perform xmlhttp request sending out to get pictures repeatly from server only when the previous http response is received.
In server side, I have created http responses which tagged with xmlhttp status =900.So I want to send out the request once the response due to previous request is received, otherwise, the program should wait until its arrival.
For example:
When I press the button somewhere in the browser, it triggers first picture (jpeg) request to server, then waiting the server's response. After getting the Http response with status marked as 900, xmlhttp will decode the response and display the picture on (image).
In the following code, by capturing the packet with wireshark, I think I success to get the right packet flow. However, the picture can not be shown in the DIV.
Can anyone help me ? Thanks a lot and a lot!
enter code here
function init(url)
{
var xmlHTTP = new XMLHttpRequest();
xmlHTTP.open('GET',url,true);
xmlHTTP.send();
xmlHTTP.responseType = 'arraybuffer';
xmlHTTP.onload = function(e)
{
var arr = new Uint8Array(this.response);
var raw = String.fromCharCode.apply(null,arr);
var b64=btoa(raw);
var dataURL="data:image/jpeg;base64,"+b64;
document.getElementById("image").src = dataURL;
};
xmlHTTP.onreadystatechange=function(){
buff(xmlHTTP.status);
}
}
buff(status){
if (status=900){
sleep(1000);
init('/images/photos/badger.jpg');
}
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
</script>
</head>
<body>
<div id="image"><h2>picture display here</h2></div>
<button type="button" onclick=buff(900)>Get Picture</button>
enter code here
There are several problems with your code.
Markup
The problem with your markup is, that you are using a div tag to display an image. The src attribute is not supported on div tags. You should use an img tag instead like this:
....
<img id="image" />
<button type="button" onclick="buff(900)">Get Picture</button>
Statuscode
Why are you using a status of 900? If the request was correct and the image was loaded, please use a status of 200.
Image loading
Why are you even using an XMLHttpRequest for loading images?
You could simply change the src attribute on the img tag and the browser will request the image.
If you want to reload the image you could just refresh the src attribute. If all images a served under the same URL, you can add a request param like the current time:
document.getElementById("image").src = "/images/photos/badger.jpg#" + new Date().getTime();
This way the browser will request the image again and won't use the one allready loaded and cached. See this answer for further info. (Actually the question is nearly the same as yours...)
Sleep function
Your sleep function will use resources because it's a loop that will constantly run for the specified time. While it runs, it will add numbers and do comparisons that are completly unnecessary.
please use something like the javascript build in setTimeout():
buff(status) {
if(status === 900) {
setTimeout(funciton(){
init('/images/photos/badger.jpg');
}, 1000);
}
}
Update: working example
I set up a working example in the code snippet. It loads images from the same url, but they are served randomly on each request by lorempixel.
I reorganized the image loading. Actually there was another problem. You startet the next image loading with onreadystatechange. This will fire for each change of the readystate and not only when the image is loaded. Therefore I start the next buff() from the onload() like so:
var xmlHTTP = new XMLHttpRequest();
xmlHTTP.open('GET',url,true);
xmlHTTP.responseType = 'arraybuffer';
xmlHTTP.onload = function(e) {
var arr = new Uint8Array(this.response);
var raw = String.fromCharCode.apply(null,arr);
var b64 = btoa(raw);
var dataURL = "data:image/jpeg;base64," + b64;
document.getElementById("image").src = dataURL;
buff(this.status);
};
xmlHTTP.send();
For convenience I added a button for stopping the loading of new images.
For your example you just have to change the imageUrl and imageStatus variables.
var imageUrl = "http://lorempixel.com/400/200/", // set to "/images/photos/badger.jpg" for your example
imageStatus = 200, // set to 900 for your example
stopped = true;
function stopLoading() {
stopped = true;
}
function loadNextImage(url) {
if(!stopped) { // only load images if loading was not stopped
var xmlHTTP = new XMLHttpRequest();
xmlHTTP.open('GET',url,true);
xmlHTTP.responseType = 'arraybuffer';
xmlHTTP.onload = function(e) {
var arr = new Uint8Array(this.response);
var raw = String.fromCharCode.apply(null,arr);
var b64 = btoa(raw);
var dataURL = "data:image/jpeg;base64," + b64;
document.getElementById("image").src = dataURL;
buff(this.status); // set the next timer when the current image was loaded.
};
xmlHTTP.send();
}
}
function buff(status) {
if (status === imageStatus) {
setTimeout(function() {
loadNextImage(imageUrl + "?" + new Date().getTime());
}, 1000);
} else {
// Status does not match with expected status.
// Therefore stop loading of further images
stopLoading();
}
}
function init() {
stopped = false;
loadNextImage(imageUrl);
}
document.getElementById("start").onclick = function(){
init(imageUrl);
};
document.getElementById("stop").onclick = stopLoading;
<img id="image" />
<button type="button" id="start">Get pictures</button>
<button type="button" id="stop">No more pictures</button>
i am trying to build a remote for a gallery by using nodejs, express & socket.io.
the structure is as follows
/index.js
/public/screen.html
/screen.js
/remote.html
/remote.js
the idea is to have a gallery of images displayed on remote.html, select one and send the selected index to screen.html by using socket.io.
as of now my code looks like this:
index.js
var express = require('express');
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io').listen(server);
app.use(express.static('public'));
server.listen(8080, function(){
// setup image gallery and stuff...
connectToServer();
});
remote.js
var socket = null;
document.addEventListener("DOMContentLoaded", function(event) {
//some stuff
connectToServer();
});
function showImage (index){ //called by events
console.log('selected incdex: ' + index);
if(socket != null) {
socket.emit('selection', {id: index});
};
}
function connectToServer(){
socket = io.connect('http://localhost:8080');
socket.on('connection', function (socket) {
var text = document.querySelector('#name');
socket.emit('newRemote', 'new remote connected');
console.log('emitted welcome');
socket.on('newScreen', function (data) {
console.log(data);
});
});
}
screen.js
var socket = null;
document.addEventListener("DOMContentLoaded", function(event) {
//some stuff
connectToServer();
});
function connectToServer(){
socket = io.connect('http://localhost:8080');
socket.on('connection', function (socket) {
var text = document.querySelector('#name');
socket.emit('newScreen', { name: name });
socket.on('newRemote', function (data) {
console.log(data);
});
});
};
when starting with node index.js i get
listening on *:8080
and when loading screen or remote.html i get
debug - client authorized
info - handshake authorized MwtGFRCZamcyKkUpK5_W
as I see it: somehow a connection is established, but:
no messages are sent / received on both ends
no logs are printed to the console for the connection events
any idea why nothing is happening?
It appears that you have mixed server-side code into your client. The 'connection' event is an event that needs to be listened to on the server, not your client files. Additionally, you cannot directly call client-side functions from your server-side code.
The below code does not work:
server.listen(8080, function(){
// setup image gallery and stuff...
connectToServer();
});
To achieve the above you will need to put the following code on your server:
server.listen(8080, function(){
socket.on('connection', function (socket) {
// setup image gallery and stuff...
socket.emit('connectToServer', myPassedParameters);
});
});
And the following listener for that event on the client:
socket.on('connectToServer', function (myPassedParameters) {
var text = document.querySelector('#name');
// stuff for client side here
});
});
In addition to the above, you cannot do calls from one client file to another client file using socket.io. You would have to do a call to the server first and then call a function on the other file once it has been loaded for the user.
I want to pass the input file from content page to extension background script, and then load it with FileReader() in the extension background script.
So in the web page I have a <input type="file"> and from onchange event I pass the file from content script to background page like this:
var myfile = document.getElementById('fileid').files[0];
chrome.runtime.sendMessage({myevent: "start", inputfile: myfile}, function(response) {});
in the background script I have this:
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
if(message.myevent==="start")
{
var reader = new FileReader();
reader.onload = function(e) {
// file is loaded
}
reader.readAsArrayBuffer(message.inputfile);
}
});
but FileReader not load it, I'm not sure if this is correct way , but all i need is to pass the input file element to background script and load it with FileReader to send it with HTTP POST from background script. Please tell me what is wrong or how to do it correctly. It will help a lot if I see a sample code, because I'm new to chrome extension development, and not so experienced.
All messages send through the Chrome extension messaging API MUST be JSON-serializable.
If you want to get the contents of a file at the background page, you'd better create a (temporary) URL for the File object, pass this URL to the background page and use XMLHttpRequest to grab its contents:
// Create URL
var url = URL.createObjectURL(myfile);
// Pass URL to background page (ommited for brevity) and load it..
var x = new XMLHttpRequest();
x.onload = function() {
var result = x.response;
// TODO: Use [object ArrayBuffer]
};
x.open('GET', url); // <-- blob:-url created in content script
x.responseType = 'arraybuffer';
x.send();
Though why do you want to send the file to the background page? Content scripts can also send cross-origin requests.
This works for chrome. You could find the whole production code here.
https://github.com/Leslie-Wong-H/BoostPic/tree/7513b3b8d67fc6f57718dc8b9ff1d5646ad03c75/BoostPic_Chrome/js
main.js:
// Crossbrowser support for URL
const URLObj = window.URL || webkitURL;
// Creates a DOMString containing a URL representing the object given in the parameter
// namely the original Blob
const blobUrl = URLObj.createObjectURL(imageBlob);
console.log(blobUrl);
chrome.runtime.sendMessage(blobUrl, (res) => {
imgUrl = res;
console.log(imgUrl);
clearInterval(refreshIntervalId);
// To prevent that it happens to halt at " Image uploading ..."
setTimeout(() => {
var imgUrlText = document.querySelector(imgUrlTextBoxId);
imgUrlText.value = imgUrl;
}, 1000);
// double check to clear interval to prevent infinite error loop of LoadingStateOne
// Hope it works.
setTimeout(() => {
clearInterval(refreshIntervalId);
}, 500);
console.log("Stop uploading state message");
background.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.startsWith("blob")) {
console.log("RECEIVED");
getBase64Url(request).then((res) => {
console.log("Arrived here");
// Acquired from https://stackoverflow.com/questions/18650168/convert-blob-to-base64/18650249#
const reader = new FileReader();
reader.readAsDataURL(res);
reader.onloadend = function () {
const base64data = reader.result;
console.log(base64data);
I am creating a HTML5 SVG editor for Chrome. As a packaged app, I implemented a save dialog using this code:
function prepareExport(){
var svg = document.getElementById("canvas");
svgDoc = svg.children;
var exported = document.querySelector('#canvasWrap').innerHTML;
/*old stuff, does not work in packed apps. well for me anyway
var output = document.querySelector(".opt");
var outputTextarea = document.querySelector(".optText");
output.style.display = "block";
outputTextarea.style.display = "none";
var dlButton = document.querySelector(".dragout");
dlButton.setAttribute("href" ,"data:image/xml+svg;base64," + window.btoa(exported));
dlButton.setAttribute("data-downloadurl" ,dlButton.dataset['downloadurl'] + window.btoa(exported));
dlButton.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('DownloadURL', this.dataset.downloadurl);
}, false);
*/
chrome.fileSystem.chooseEntry({type: 'saveFile'}, function(writableFileEntry, unused) {
writableFileEntry.createWriter(function(writer) {
writer.onerror = errorHandler;
writer.onwriteend = function(e) {
console.log('write complete');
};
writer.write(new Blob([exported], {type: 'image/svg+xml'}));
}, errorHandler);
});
}
I ran this function using a button, Export SVG, and guess what? the dialog did not appear. I do not know why and this is my javascript console:
http://prntscr.com/1uklw7
This is probably a permission error. Have you added the write filesystem permission to your manifest? See http://developer.chrome.com/apps/fileSystem.html for details.
If that isn't the problem, you can get more details from chrome.lastError in your callback. See http://developer.chrome.com/apps/runtime.html#property-lastError for details of this.
Also you might want to check out the file system sample: https://github.com/GoogleChrome/chrome-app-samples/tree/master/filesystem-access.