Pouchdb Unable to get attachmentfrom local machine - javascript

I'm using the below code to store image to pouchdb, result.base_64 contains the blob. When I inspect using PouchDB Inspector it shows db size as zero bite and on clicking on the attachment it shows file not found
var db = new PouchDB('xyz');
db.putAttachment('skul', 'skul', result.base_64, 'image/jpg').then(function() {
return db.get('skul', {
attachments: true
});
}).then(function(doc) {
console.log(doc);
});
tried this for getting attachment
db.getAttachment('skul', 'skul' function(err, blob_buffer) {
if (err) {
return console.log(err);
} else {
// console.log(blob_buffer);
var url = URL.createObjectURL(blob_buffer);
var img = document.createElement('img');
img.src = url;
document.body.appendChild(img);
}
})
this displays the image on browser but url of the image is
src="blob:http://localhost:8000/d1388aaa-f2c8-45ae-af39-e2b384e25c7c"
which is referring to the server not local machine

Did you verify that result.base_64 isn't an empty string or that it's valid base64?
We have live working examples in the docs showing how to insert base64 data. You can run these examples in your browser and confirm that they work. :) https://pouchdb.com/guides/attachments.html

Related

Vue js not rendering image with base64 or binary data from Express API response

My Vue js component receives images via express API call as base64 or raw binary data, but it does not display (render) it.
I can see the image data in the variable given to img src, but for some reason it never renders it, only showing the "broken image icon".
Screenshot of broken image icon
This is the frontend component layout:
<img src="srcimg" v-if="srcimg">
The data mounted and watch functions call the async getsrcimg function which makes the Express API call:
data () {
return {
srcimg: null,
...
mounted () {
if (this.name) {
this.getsrcimg(this.name)
}
},
watch: {
name: function () {
if (this.name) {
this.getsrcimg(this.name)
}
}
},
And the function that receives the image via api:
async getsrcimg (name) {
try {
var getImgRes = await axios.post('http://localhost:5000/get_img?img_path=' + name)
const { data } = getImgRes
this.srcimg = 'data:image/png;base64,' + data
} catch (err) {
console.log(err)
this.message = err.response.data.error
}
}
The server backend function:
app.post('/get_img', (req, res) => {
var img_path = __dirname + req.query.img_path
var fs = require('fs');
const bitmap = fs.readFileSync(img_path);
const base64 = new Buffer.from(bitmap).toString("base64");
res.send(base64); // Send base64 instead of the raw file binary
});
I can see in the console log, as well as display the this.srcimg data available in this format:
data:image/png;base64,77+9UE5HDQoaCg...
But still, the HTML img element does not display the image.
I know the image is being sent correctly because I can see it in the browser inspect Network response preview so it probably is an issue within Vue frontend.
I have also tried to send the image raw binary data from server using res.sendFile and then loading it in the frontend using:
const blob = new Blob([data], {type: 'image/png'})
var imgName = 'image_name_example'
var file = new File([blob], imgName, { type: 'image/png' })
let reader = new FileReader()
reader.readAsDataURL(file)
reader.addEventListener('load', this.processReaderIMG, false)
...
processReaderIMG: function (readerData) {
this.srcimg = readerData.target.result
}
This also gives me the image data but the img element does not display it still.
Any help would be much much appreciated!
If this really is your frontend component:
<img src="srcimg" v-if="srcimg" />
, it attempts to render "srcimg", the actual string, instead of the contents of the reactive variable named srcimg.
And "srcimg" is not a valid source. It's not a url and it's not binary data.
You probably want to use :src instead of src:
<img :src="srcimg" v-if="srcimg" />
, shorthand for
<img v-bind:src="srcimg" v-if="srcimg" />
Docs: v-bind.
If the above is not the issue, test that the binary data is valid (manually).
Other possible causes:
the binary data might contain line breaks which break the HTML (highly improbable, but worth a check)
the response has a different encoding than your page
the response is already prefixed with 'data:image/png;base64,'
If everything fails, perhaps you could share the exact response of the server. There has to be something wrong with it, which you're missing.

WebKitSubtleCrypto error when using dispatchMessage in Safari Extension

I am attempting to build a Safari extension to share screenshots of webpages but when I try to pass the image back to Swift I get an error that makes Safari unstable and kills my task mid-process.
The idea is that when a user taps a tool bar button any selected text and a screenshot of the webpage are saved. I am trying to pass both of those through the userInfo dictionary. If I run my code as is with the dispatchMessage call commented out I do not see any errors. If I uncomment the dispatch call I see the following error:
WebKitSubtleCrypto is deprecated. Please use SubtleCrypto instead.
Here is my js code:
document.addEventListener("DOMContentLoaded", function(event) {
safari.self.addEventListener("message", handleMessage);
});
function handleMessage(event) {
var selectedText = window.getSelection().toString();
var screenshot;
if (window.top === window) {
html2canvas(document.getElementsByTagName('html')).then(function(canvas) {
screenshot = convertCanvasToImage(canvas);
console.log("canvas image: " + screenshot)
safari.extension.dispatchMessage("test", {"selectedText": selectedText, "screenshot" : canvas});
});
}
}
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
The html2canvas (latest - 0.5.0-beta4) script is in another file packaged with the extension.
Edit 1
After some more testing it looks as though this error only has to do with passing the 'screenshot' object in the messageDipatch call. If I take out screenshot and only pass the selectedText data it works as expected. I have also tried to pass the screenshot through as a canvas instead of running it through the 'convertCanvasToImage()' call but I am getting the same error with that.
The issue ended up being related to how I was init'ing the image data before converting the canvas to a data url.
Going from:
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
to:
function convertCanvasToImage(canvas) {
var imageData = canvas.toDataURL("image/png")
return imageData;
}
resolved the issue.
On the Swift side this is how I am decoding that data (be cautious of the all the forcing in this):
let imageString = userInfo?["screenshot"] as! String
let imageData = NSData.init(contentsOf: NSURL(string: imageString) as! URL)
let image = NSImage(data: imageData as! Data)

Nativescript get base64 data from native image

I try to use https://github.com/bradmartin/nativescript-drawingpad for saving a signature to my backend. But I am simply not capable to find a solution to get some "useful" data from getDrawing(), which returns a native image Object, for example UIImage on iOS.
I would love to "convert" the image data to some base64 (png, or whatever) string and send it to my server.
I tried someting like:
var ImageModule = require("ui/image");
var ImageSourceModule = require("image-source");
elements.drawingpad.getDrawing().then(function(a){
var image = ImageSourceModule.fromNativeSource( a );
api.post("sign", image.toBase64String());
});
I also tried to post simply a like seen in the demo stuff.
I would really love to see a demo of how to get my hands on the "image data" itself.
thanks!
Thanks to #bradmartin I found the solution:
var image = ImageSourceModule.fromNativeSource(a);
var base64 = image.toBase64String('png');
Actually after lots of digging with tons of error, I finally figured it out.
First you have to require the nativescript-imagepicker source module and also the nativescript image source.
var imagepicker = require("nativescript-imagepicker");
var ImageSourceModule = require("tns-core-modules/image-source");
a case where you want to update a user profile and also send a base64 string to your backend for processing
function changeProfileImage(args) {
var page = args.object;
var profile = page.getViewById("profile-avatar");
var context = imagepicker.create({ mode: "single" });
context.authorize().then(function() {
return context.present();
}).then(function(selection) {
profile.background = `url(${selection[0]._android})`;
profile.backgroundRepeat = `no-repeat`;
profile.backgroundSize = `cover`;
ImageSourceModule.fromAsset(selection[0]).then(image => {
var base64 = image.toBase64String('png');
// console.log(base64);
uploadMediaFile(base64);
});
}).catch(function (e) {
// process error
console.log(e);
});
}

Displaying images from private subdirectory in Meteor

I have a set of images I am storing in my /private sub-directory, I am trying to retrieve the data inside a server method and sending the data back to the client to be displayed.
How can I do that?
I have an image named test.png inside /private/photos. Here's what I've tried.
/client/test.js
Template.test.onRendered(function () {
Meteor.call('returnPhoto', 'photos/test.png', function (e, data) {
console.log(data);
console.log(window.btoa(data));
$('#imgContainerImg').attr('src', 'data:image/png;base64,' + window.btoa(data));
});
})
/server/methods.js
returnPhoto: function (assetPath) {
return Assets.getText(assetPath);
return Assets.getBinary(assetPath);
}
I tried both Assets.getText and Assets.getBinary, the first gives me some binary gibberish, and the second gives me an array of numbers. Using the btoa function doesn't work regardless.
I have looked at the CollectionFS package, but I do not need to upload the pictures and store them all in a collection. I'd like the images to be available as soon as I put them in that directory, without having to call myFSCollection.insert.
Using the following, I was able to get images from the private directory, send it over to the client as a byte array, which then gets converted into a base64 string and displayed as data URL.
client/test.js
Template.test.onRendered(function () {
Meteor.call('returnPhoto', 'photos/test.png', function (e, data) {
var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(data)));
$('#imgContainerImg').attr('src', 'data:image/png;base64,' + base64String);
});
})
server/methods.js
returnPhoto: function (assetPath) {
return Assets.getBinary(assetPath);
}
This is the solution I work with:
client/main.js
const imagesLookup = new ReactiveDict();
Template.registerHelper('img', function(src) {
Meteor.call('image', src, (err, img)=> {
imagesLookup.set(src, img);
});
return imagesLookup.get(src);
});
client/main.html
<template="stuffWithImage">
<!-- src is the path of the image in private -->
<img src="{{img src}}"/>
</template>
imports/methods.js
Meteor.methods({
image(src){
//validate input and check if use is allowed to see this asset
if(Meteor.isClient){
//return some loading animation
}
const buffer = new Buffer(Assets.getBinary(src), 'binary');
const magicNumber = buffer.toString('hex',0,4)
const base64String = buffer.toString('base64');
return `data:image/${getImageType(magicNumber)};base64,${base64String}`;
}
});
function getImageType(magicNumber){
if (magicNumber === 'ffd8ffe0') {
return 'jpeg';
}
//check for other formats... here is a table: https://asecuritysite.com/forensics/magic
}

Meteor CollectionFS: Make sure the image is uploaded before displaying

I am using the CollectionFS package with the S3 adapter and I've looked at a few different solutions but cannot get this to work right.
The problem: Even though the file/image is being uploaded to S3 successfully, the callback for a successful upload is triggered before it is safe to display the image. This causes a broken image to be displayed sometimes.
I found out about the fileObj.once("uploaded", function(){}) callback but it seems "uploaded" basically means sending the image to the server. The S3 uploading does not happen by then. A temporary workaround I found is to just to just have setTimeout for 3-4 seconds but this is not reliable.
Here is my upload code:
FS.Utility.eachFile(event, function(file) {
Session.set('profilePhotoUploaded', false);
var newFile = new FS.File(file);
newFile.metadata = {owner: Meteor.userId()};
ProfileImages.insert(newFile, function (err, fileObj) {
if (err){
console.log("error! - " + err);
} else {
// handle success depending what you need to do
var userId = Meteor.userId();
// This does NOT run when image is stored in S3. I think it runs when the image reached the app server.
fileObj.once("uploaded", function () {
// timeout of 3 seconds to make sure image is ready to be displayed
// --- This is not a good solution and it image does is not always ready
setTimeout(function(){
var uploadedImage = {
"profile.image.url": "/cfs/files/profileImages/" + fileObj._id
};
Meteor.users.update(userId, {$set: uploadedImage});
Session.set('profilePhotoUploaded', true);
}, 3000);
console.log("Done uploading!");
});
}
});
});
Is there a different callback to check if the image has actually been stored in S3? I tried fileObj.once("stored", function(){}) but that does not work.
The issue is that the stored hook will fire when the original image is saved on the server, so if you're creating multiple copies (thumbnails) this hook will fire before your thumbnails are stored. You can check which version of the thumbnail was stored by checking the storeName argument. In the server side file, where you define the ProfileImages collection add the following code, replacing 'profilePhotoLarge' with the name assigned to your FS.Store.S3 store:
ProfileImages.on('stored', Meteor.bindEnvironment(function(fileObj, storeName) {
if (storeName === 'profilePhotoLarge') {
Meteor.users.update({_id: fileObj.metadata.owner}, {
$set: {
'profile.image.url': 'https://your AWS region domain/your bucket name/your folder path/' + fileObj._id + '-' +fileObj.name()
}
});
}
}, function() { console.log('Failed to bind environment'); }));
For profile photos I created an S3 bucket and set the permissions to allow anyone to read the files, so I'm storing the URL to the image on S3, which may not be correct in your case. Since the user object is reactive on the client side this update will cause the profile photo to update automatically.
I found that fileObj.hasStored("profileImages") specifies exactly when the image is stored on S3. So after starting the upload process, I just start a timer every 1 second to check if it is saved. This might not be the best solution but this is what worked for me.
FS.Utility.eachFile(event, function(file) {
Session.set('profilePhotoUploaded', false);
var newFile = new FS.File(file);
newFile.metadata = {owner: Meteor.userId()}; // TODO: check in deny that id is of the same user
ProfileImages.insert(newFile, function (err, fileObj) {
if (err){
console.log("error! - " + err);
} else {
// handle success depending what you need to do
var userId = Meteor.userId();
// Timer every 1 second
var intervalHandle = Meteor.setInterval(function () {
console.log("Inside interval");
if (fileObj.hasStored("profileImages")) {
// File has been uploaded and stored. Can safely display it on the page.
var uploadedImage = {
"profile.image.url": "/cfs/files/profileImages/" + fileObj._id
};
Meteor.users.update(userId, {$set: uploadedImage});
Session.set('profilePhotoUploaded', true);
// file has stored, close out interval
Meteor.clearInterval(intervalHandle);
}
}, 1000);
}
});
});

Categories