I've been beating my head over this and I can't find a proper solution.
I want to be able to upload images to the server via socket.io emit and save them to a MongoDB database later. How do I do this? I've seen people doing it with base64 encoding but I can't figure out how that exactly works, there are other questions on this website asking about sending an image to client from server via socket.io but none about this. All help is appreciated. <3
Goal: To upload an image to server with socket.emit('image', someimagefile) or similar.
I'd really appreciate if you provide a similar way to send an image to the client.
As you mentioned, you can convert the image to base64 using FileReader.readAsDataURL and send the encoded string, and decode it on the server:
document.getElementById('file').addEventListener('change', function() {
const reader = new FileReader();
reader.onload = function() {
const base64 = this.result.replace(/.*base64,/, '');
socket.emit('image', base64);
};
reader.readAsDataURL(this.files[0]);
}, false);
socket.on('image', async image => {
const buffer = Buffer.from(image, 'base64');
await fs.writeFile('/tmp/image', buffer).catch(console.error); // fs.promises
});
Or better use FileReader.readAsArrayBuffer to get an array of bytes that you'll send to the server.
document.getElementById('file').addEventListener('change', function() {
const reader = new FileReader();
reader.onload = function() {
const bytes = new Uint8Array(this.result);
socket.emit('image', bytes);
};
reader.readAsArrayBuffer(this.files[0]);
}, false);
socket.on('image', async image => {
// image is an array of bytes
const buffer = Buffer.from(image);
await fs.writeFile('/tmp/image', buffer).catch(console.error); // fs.promises
});
To receive from the server:
// Server side
socket.emit('image', image.toString('base64')); // image should be a buffer
// Client side
socket.on('image', image => {
// create image with
const img = new Image();
// change image type to whatever you use, or detect it in the backend
// and send it if you support multiple extensions
img.src = `data:image/jpg;base64,${image}`;
// Insert it into the DOM
});
Base64 can work, but one more thing to keep in mind is that socket buffer size limit is 1 MB. (This can be increased according to docs).
So I guess if the file size is huge, its better to stream it with something like socket.io-stream
i don't know if any one is looking for it anymore but I made possible to send media via socket.io... here is the code:
// sending media from client side
$("#send_media").change(function (e) {
var data = e.originalEvent.target.files[0];
var reader = new FileReader();
reader.onload = function (evt) {
var msg = {};
msg.file = evt.target.result;
msg.fileName = data.name;
socket.emit("base64 file", msg);
console.log(msg)
};
reader.readAsDataURL(data);
});
// showing media to ui
socket.on("base64 image", (msg) => {
console.log("as", msg);
$(".messages")
.append(`<img src=${msg.file} alt="Red dot" />`);
scrollToBottom();
});
// sending media from server side
socket.on("base64 file", function (msg) {
console.log("received base64 file from server: " + msg.fileName);
socket.username = msg.username;
io.to(roomId).emit('base64 image', //exclude sender
// io.sockets.emit(
// "base64 file", //include sender
{
file: msg.file,
fileName: msg.fileName,
}
);
});
Related
I have image URL like this - https://graph.facebook.com/3938027626271800/picture?type=normal.
I want to create a helper function in which I will pass the image URL as a parameter and it will return base 64 string. Right now I am following this approach but this approach converts the URL to base64 but it is not returning the base 64.
function toDataUrl(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
callback(reader.result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
toDataUrl('https://graph.facebook.com/3938027626271800/picture?type=normal', function(myBase64) {
console.log(myBase64); // myBase64 is the base64 string
});
What do you mean return base64 not base 64?
What's the expected output?
It seems your code works well, i wrote in another approach got same results as you did.
async function getBase64ImageFromUrl(imageUrl) {
var res = await fetch(imageUrl);
var blob = await res.blob();
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.addEventListener("load", function () {
resolve(reader.result);
}, false);
reader.onerror = () => {
return reject(this);
};
reader.readAsDataURL(blob);
})
}
getBase64ImageFromUrl('https://graph.facebook.com/3938027626271800/picture?type=normal')
.then(result => console.log(result))
.catch(err => console.error(err));
As images may contain sensitive data, it's not possible to read images loaded from a different domain unless they allow it with CORS by specifying header
Allow-Content-Allow-Origin: *
Your example works just fine because the facebook image provides this header but it will not work for all images from external domains.
Your second thought may be to try to draw an image into canvas and then get data back but luckily for user's safety this is also prohibited:
https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
The only way is to create a proxy on your domain to download the image from external source, it can already convert it to base64 for you on the backend as well. This is secure because the image is no longer downloaded in the browser user context, will not use his private cookies and IP address and so will not contain any sensitive data.
In HTML file:
<input type="file" (change)="selectedImage($event)" accept="image/*" name="Image">
In TS file:
public async selectedImage(event) {
const reader = new FileReader();
if (event.target.files && event.target.files.length) {
const [file] = event.target.files;
reader.readAsDataURL(file);
reader.onload = async () => {
reader.result // This is Image base64
};
}
}
when i get the image from the input
i have to convert it to a buffer to make some operations with the image, so as a result a i have a buffer instead of file.
im using FileCollection in meteor to store the image in mongo collection
uploadIt(e) {
e.preventDefault();
var reader = new FileReader();
var buffer;
var file = e.currentTarget.files[0];
if (e.currentTarget.files && e.currentTarget.files[0]) {
reader.onload = function(e){
buffer = new Uint8Array(reader.result);
// some operations over the buffer
};
reader.readAsArrayBuffer(file);
if (file) {
let uploadInstance = CourseFilesCollection.insert({
file: buffer,
..
..
})
}
}
but when i insert it got this error
message: "[FilesCollection] [insert] Have you forget to pass a File itself?
the code originally was
if (file) {
let uploadInstance = CourseFilesCollection.insert({
file: file,
..
..
})
}
but since i had to perfom operations over the the image i need to someway conver the buffer to file
any ideas how to solve this ?
Short answer
use the file constructor to turn bits back to a file container:
file: new File([buffer], file.name, file)
you could try using blob also with wider browser support... but if you want to use the latest tech, then:
async uploadIt (evt) {
evt.preventDefault()
const file = evt.currentTarget.files[0]
if (!file) return
const buffer = new Uint8Array(await file.arrayBuffer())
// some operations over the buffer
const uploadInstance = CourseFilesCollection.insert({
file: new File([buffer], file.name, file)
})
}
I have a meteor application and in this one I get a base64 image. I want to save the image on a Digital Ocean instance, so I would convert it in a png or an other image format and send it to the server to get an url of the image.
But I didn't find a meteor package that does this.
Do you know how I can do that ?
I was running into a similar issue.
run the following:
meteor npm install --save file-api
This will allow the following code on the server for example:
import FileAPI from 'file-api';
const { File } = FileAPI;
const getFile = function(name,image){
const i = image.indexOf('base64,');
const buffer = Buffer.from(image.slice(i + 7), 'base64');
const file = new File({buffer: buffer, name, type: 'image/jpeg'});
return file;
}
Simply call it with any name of file you prefer, and the base64 string as the image parameter.
I hope this helps. I have tested this and it works on the server. I have not tested it on the client but I don't see why it wouldn't work.
I solved my problem using fs.writeFile from File System.
This is my javascript code on client side, I got a base64 image (img) from a plugin and when I click on my save button, I do this :
$("#saveImage").click(function() {
var img = $image.cropper("getDataURL")
preview.setAttribute('src', img);
insertionImage(img);
});
var insertionImage = function(img){
//some things...
Meteor.call('saveTileImage', img);
//some things...
}
And on the server side, I have :
Meteor.methods({
saveTileImage: function(fileData) {
var fs = Npm.require('fs');
var path = process.env.PWD + '/var/uploads/';
base64Data = fileData.replace(/^data:image\/png;base64,/, "");
base64Data += base64Data.replace('+', ' ');
binaryData = new Buffer(base64Data, 'base64').toString('binary');
var imageName = "tileImg_" + currentTileId + ".png";
fs.writeFile(path + imageName, binaryData, "binary", Meteor.bindEnvironment(function (err) {
if (err) {
throw (new Meteor.Error(500, 'Failed to save file.', err));
} else {
insertionTileImage(imageName);
}
}));
}
});
var insertionTileImage = function(fileName){
tiles.update({_id: currentTileId},{$set:{image: "upload/" + fileName}});
}
So, the meteor methods saveTileImage transform the base64 image into a png file and insertionTileImage upload it to the server.
BlobUrl, would it be a better option for you?
Save the images to a server as you like in base64 or whatever, and then when you are viewing the image on a page, generate the blobUrl of it. The url being used only at that time, preventing others from using your url on various websites and not overloading your image server ...
I am able to upload image file to S3 using parse server. (by creating parse file from base64 image data and doing save() on parse file)
How can I do the same thing for a video file? I am doing this using parse-server js library in Ionic 2 app with typescript. The below code worked for images.
let file = new Parse.File("thumbnail", { base64: imageData });
file.save().then(() => {
// The file has been saved to Parse.
console.log("File uploaded....");
}, (error) => {
// The file either could not be read, or could not be saved to Parse.
console.log("File upload failed.");
});
In case of a video file, I have the file location received from cordova media capture callback. Help me in uploading the video file.
Thank you
here is my solution after days of research.
it works for iphone.
the important statement is this:
data=data.replace("quicktime","mov");
var options = { limit: 1, duration: 30 };
navigator.device.capture.captureVideo(function(files){
// Success! Audio data is here
console.log("video file ready");
var vFile = files[0];
console.log(vFile.fullPath);
///private/var/mobile/Containers/Data/Application/7A0069EB-F864-438F-A685-A0DAE97F8B2D/tmp/capture-T0x144510b50.tmp.GfXOow/capturedvideo.MOV
self.auctionvideo = vFile.fullPath; //localURL;
console.log(self.auctionvideo);
var fileReader = new FileReader();
var file;
fileReader.onload = function (readerEvt) {
var data = fileReader.result;
data=data.replace("quicktime","mov");
console.log(data);
//data:video/quicktime;base64,AAAAFGZ0
console.log(data.length);
self.auctionvideo=data;
self.videofile = {base64:data};
};
//fileReader.reasAsDataURL(audioFile); //This will result in your problem.
file = new window.File(vFile.name, vFile.localURL,
vFile.type, vFile.lastModifiedDate, vFile.size);
fileReader.readAsDataURL(file); //This will result in the solution.
// fileReader.readAsBinaryString(file); //This will result in the solution.
},
function(error){
},
options);
I am using ngCordova Capture to write this code by recording audio and send the base64 somewhere (via REST). I could get the Capture Audio to work but once it returns the audioURI, I cannot get the data from the filesystem as base64. My code is below:
$cordovaCapture.captureAudio(options).then(function(audioURI) {
$scope.post.tracId = $scope.tracId;
$scope.post.type = 'audio';
console.log('audioURI:');
console.log(audioURI);
var path = audioURI[0].localURL;
console.log('path:');
console.log(path);
window.resolveLocalFileSystemURL(path, function(fileObj) {
var reader = new FileReader();
console.log('fileObj:');
console.log(fileObj);
reader.onloadend = function (event) {
console.log('reader.result:');
console.log(reader.result);
console.log('event.result:');
console.log(event.result);
}
reader.onload = function(event2) {
console.log('event2.result:');
console.log(event2.target.result);
};
reader.readAsDataURL(fileObj);
console.log(fileObj.filesystem.root.nativeURL + ' ' + fileObj.name);
$cordovaFile.readAsDataURL(fileObj.filesystem.root.nativeURL, fileObj.name)
.then(function (success) {
console.log('success:');
console.log(success);
}, function (error) {
// error
});
});
Here is the output in console log:
So how do I get the base64 data from the .wav file?
I have been reading these links:
PhoneGap FileReader/readAsDataURL Not Triggering Callbacks
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
http://jsfiddle.net/eliseosoto/JHQnk/
http://community.phonegap.com/nitobi/topics/filereader_onload_not_working_with_phonegap_build_2_5_0
Had same problem, which I fixed using both the Cordova Capture and Cordova File plugin.
navigator.device.capture.captureAudio(function (audioFiles) {
var audioFile = audioFiles[0],
fileReader = new FileReader(),
file;
fileReader.onload = function (readerEvt) {
var base64 = readerEvt.target.result;
};
//fileReader.reasAsDataURL(audioFile); //This will result in your problem.
file = new window.File(audioFile.name, audioFile.localURL,
audioFile.type, audioFile.lastModifiedDate, audioFile.size);
fileReader.readAsDataURL(file); //This will result in the solution.
});