Can we store image in vuex? Should we do it? - vue.js

I am trying to store profile thumbnail for easy access. Since I want to minimize resource consumption by eliminating the methods to fetch the image path every time the user reloads the page. Can we store image data in vuex? If so, should we do it?

Yes you can!
After the image response, pass the image to URL.createObjectURL() (Doc). This will cache the image in the browser and will give you a local image url, which you can then re-use:
const imageUrl: string = URL.createObjectURL(response.data);
if (imageUrl) {
// Update your store with an object like this:
{ id: imageId, url: imageUrl };
}
Then you can search your imageUrl[] state, where you saved this, by the id and retrieve the URL again.

Related

Expo: How can i save an image (from ImagePicker) into an album without duplicates?

In my app a user can select a photo (from gallery or take one with the camera) and I want to save this photo into an album for my app.
I'm using expo 44.
I'm testing on android only. The version is 11.
My app.json has configured the proper permissions: "CAMERA", "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE"
This is a snippet of my code
// I pick the image from gallery or take a picture with the camera
const image = pickFromGallery ?
await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images })
:
await ImagePicker.launchCameraAsync()
if (!image.cancelled) {
// I create the asset
const asset = await MediaLibrary.createAssetAsync(image.uri)
// I create a new album with the asset
// The third argument is false because I want to move the image from the place that was created with "createAssetAsync" to the album
// so there is no duplicates
const album = await MediaLibrary.createAlbumAsync('myAppAlbum', asset, false)
// I get the first album asset (just created so there is only one) in order to optain the image uri on the album
const albumAssets = await MediaLibrary.getAssetsAsync({
album: album,
first: 1
})
// I get the image uri in the album
const imageUri = albumAssets.assets[0].uri
}
So whats happening is:
I take the picture (or select it from gallery), this gives me an object with a uri that points to the image in memory
I save the picture with "createAssetAsync" using this uri, this creates a file on /DCIM, and returns the asset ref
Now I use the asset ref to create a new album, depending on the third parameter (true/false) it will behave like this:
true: the asset is copied from /DCIM to the albums directory (in my case /Pictures/myAppAlbum). This is no good since it is creating duplicates of the image
false: the asset is moved from /DCIM to the albums directory but it ask from permissions to modify the file. This is annoying since every time the user does this action he'll be asked for permission, and I'm unable to find what permission to request beforehand so the user only has to agree once.
Is there a way to either:
Save the image directly into the album aboiding the create of the file on /DCIM
Ask the user for permissions only once instead of everytime

VueJS and Image CDNs: How to check when image is done being optimized through the Image CDN?

I might be over thinking this, but I got a an avatarprofile component that lets a user update their avatar image. I am using Google Cloud storage as the image hosting and then I am using an image CDN (imagekit) to handle the image optimization/caching. All works good...however, I do have a minor annoyance that I was wondering if someone could help me with:
First, here's my code (will help explain what I am having issue with):
async avatarChangeHandler(e) {
this.overlayShow = true //<-- show a loading indicator
try {
if (e.target.files.length) {
this.image = await new Promise((resolve) => {
const reader = new FileReader()
reader.onload = (e) => {
resolve(e.target.result)
}
reader.readAsDataURL(e.target.files[0])
})
await this.photoUpload() //<-- upload image to Google storage and return new image URL
await this.updateAvatar() //<-- update user's profile in the database with new URL
await this.$store.dispatch('getUserProfile', this.currentUser) //<-- grab updated profile
console.log('updating...done')
this.overlayShow = false //<-- terminate the loading indicator
The problem is that the user profile is updated with the new image URL and fetched faster than the image CDN...and so that means the overlayShow is already set to false and still shows the old image for a second or so (depending on level of optimization needed).
What can I do to make sure the overlayShow is not set to false until the image CDN is done with the new image? Thanks and I realize this may not be feasible, but looking for advice or suggestions on approach. Thanks!

Upload image need to refresh using $emit / $on in Vue

I have a method that calls the user data via axios
// method name getUser()
const user = await axios.get(`/user/${this.id}`)
this.id = user.data.data.id
this.name = user.data.data.name
this.email = user.data.data.email
I then use that in the mounted so if user visits /profile/id
it'll load the user data
mounted() {
this.getUser()
}
I tried to upload an image and I emit the event using global event bus once the image is successfully uploaded.
this.$event.$emit('IMAGE_UPLOAD')
Then catch that on the mounted too
mounted () {
// if I remove this it works, but I need to preload the data of the user
this.getUser()
this.$event.$on('IMAGE_UPLOAD', () => {
this.getUser()
})
}
my problem is it doesn't change the image meaning I still need to refresh the page if I call the this.getUser() too inside the mounted.
So I'm wondering how to work around this.
Thanks!
Since the url and name of the image does not change when the new image is uploaded the image in the browser is not updated. So what I have done in the past is a little trick to essentially change the url to the image by adding a unique query parameter. So use a data property for the location of your user image and in your method where you update the users data also update the img url and add something unique to the query parameter. I usually use new Date().getTime(). So you will end up with something like /img/user-xxxx.png?1559289852686
data(){
userImg: '/img/user-xxxx.png'
},
methods:{
getUser(){
//... get your user data
.then((data)=>{
this.userImg = data.user.img +'?'+ new Date().getTime();
})
}

How to hash image data in React-Native (Expo)?

My intent:
I want my app to upload images to S3. If image already exists, server should record a reference to existing image rather than asking for an upload of another copy.
How I imagine that works:
Hash image data
Send hash to server with request for signed url (to upload to AWS S3)
If hash matches something already stored, reference it and tell app
Initial thoughts:
Use imageEditor.cropImage to get image into ImageStore, which will give me an appropriate uri. Then use getBase64ForTag(uri, success, failure) to retrieve base64 data for a hash calculation.
The problem:
According to the answer on this question, this process is not efficient in the least. The usual solution would be to use native methods, as described in the answer to this question, however I do not want to eject my Expo app for this feature.
My Question:
Is there a better way to hash image data? Or more fundamentally, is there a better way of ensuring that identical images are not duplicated in S3 storage?
EDIT 2020-10-21 :
The library updated itself, and you should now call:
_hashImage = async (imageUri) => {
return await FileSystem.getInfoAsync(imageUri, { md5: true } );
}
ORIGINAL:
It turns out that Expo provides this out of the box.
Expo.FileSystem.getInfoAsync
myImageHashFunction = async (imageUri) => {
let fsInfo = await Expo.FileSystem.getInfoAsync(imageUri, [{ md5: true }] )
console.log(fsInfo.md5)
}
If you are still looking for a solution:
This is how I got it working - create a base64 of the image and then create a hash of it.
import * as FileSystem from 'expo-file-system';
import * as Crypto from 'expo-crypto';
let info = await FileSystem.readAsStringAsync(imageUri,
{ encoding: FileSystem.EncodingType.Base64 });
const hashData = await Crypto.digestStringAsync (
Crypto.CryptoDigestAlgorithm.MD5,
info
)

Titanium - save remote image to filesystem

I'm building an app with titanium and I would like to save in the phone, the user's profile picture. In my login function, after the API response, I tried to do :
Ti.App.Properties.setString("user_picture_name", res.profil_picture);
var image_to_save = Ti.UI.createImageView({image:img_url}).toImage();
var picture = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, res.profil_picture); //As name, the same as the one in DB
picture.write(image_to_save);
And in the view in which I want to display the image :
var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,Ti.App.Properties.getString("user_picture_name") );
var image = Ti.UI.createImageView({
image:f.read(),
width:200,
height:100,
top:20
});
main_container.add(image);
But the image doesn't appears. Could someone help me ?
Thanks a lot :)
There are 2 issues with your code:
1 - You cannot use toImage() method unless your image view is rendered on UI stack or simply on display. Rather you should use toBlob() method.
2 - Point no. 1 will also not work the way you are using because you cannot directly use toBlob() method until or unless the image from the url is completely loaded, means until it's shown on image view. To check when the image is loaded, use Ti.UI.ImageView onload event
But, there's another better approach to do such type of tasks.
Since you have the image url from your Login API response, you can use this url to fetch image from http client call like this:
function fetchImage() {
var xhr = Ti.Network.createHTTPClient({
onerror : function() {
alert('Error fetching profile image');
},
onload : function() {
// this.responseData holds the binary data fetched from url
var image_to_save = this.responseData;
//As name, the same as the one in DB
var picture = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, res.profil_picture);
picture.write(image_to_save);
Ti.App.Properties.setString("user_picture_name", res.profil_picture);
image_to_save = null;
}
});
xhr.open("GET", img_url);
xhr.send();
}
You don't need to manually cache remote images, because
Remote images are cached automatically on the iOS platform and, since
Release 3.1.0, on the Android platform.
[see docs here & credit to Fokke Zandbergen]
Just use the remote image url in your UI, at first access Titanium will download and cache it for you; next accesses to the same image url will actually be on the automatically cached version on local device (no code is best code)
Hth.