Closed centrual closed 6 years ago
I wrote my code for my project:
1) Waits for deviceready event.
2) Tries to find in phone cache first
3) If not found, tries to download from remote url, if it success, saves to cache and loads from cache on next.
4) If it can't download, displays local-fallback-image
.
It supports placeholder image for before load.
Sample Usage:
<script>
export default {
data() {
return {
defaultPlaceholderImage: require('assets/static/img/defaultPlaceholderImage.jpg')
}
},
methods: {
openBigImage() {
// Opens lightbox etc.
}
}
}
</script>
<template>
<offline-image @click="openBigImage" src="https://avatars3.githubusercontent.com/u/2001424?v=3&s=460" :placeholder="defaultPlaceholderImage" :local-fallback-image="defaultPlaceholderImage"></offline-image>
<template>
offlineImage.vue
<script>
export default {
name: 'offlineImage',
render: function (c) {
if (this.img != '') {
return c('img', {
attrs: {
src: this.img
},
on: {
click: this.onClick
}
})
}
let attrs = {}
if (this.placeholderClass != "")
attrs = { "class": this.placeholderClass }
return c('div', {
"attrs": attrs,
on: { click: this.onClick }
})
},
props: {
localFallbackImage: {"type": String},
placeholder: {"type": String},
placeholderClass: {
"type": String,
"default": ""
},
timeout: {
"type": Number,
"default": 3000
},
src: {
"type": String,
"required": true
}
},
data() {
return {
img: "",
timer: null,
cacheFailCalled: false
}
},
methods: {
onClick(event) {
this.$emit('click', event)
},
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
},
randomFileName(imageUrl) {
let fileExt = "",
fullFileName = "",
allChars = "qwertyuopasdfghjklizxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789"
if (imageUrl.indexOf('?') > -1) {
let splitted = imageUrl.split('?', 1)
if (Array.isArray(splitted)) {
splitted = splitted[0].split('.')
fileExt = splitted[splitted.length - 1]
}
} else {
let splitted = imageUrl.split('.')
fileExt = splitted[splitted.length - 1]
}
if (fileExt.endsWith('/'))
fileExt = fileExt.substr(0, fileExt.length - 1)
for (let x = 0; x < 20; x++) {
fullFileName += allChars.charAt(this.getRandomInt(0, allChars.length - 1))
}
return `${fullFileName}.${fileExt}`
},
downloadAndCacheImage() {
let newFilePath = cordova.file.dataDirectory + this.randomFileName(this.src),
ft = new FileTransfer()
ft.download(this.src, newFilePath, (entry) => {
localStorage.setItem(this.src, newFilePath)
this.img = entry.toInternalURL()
}, () => {
this.img = this.localFallbackImage
})
},
testImageAndSetIfSucceed( src, onError ) {
let image = new Image()
image.onload = () => {
console.log('onload called')
this.img = src
if (this.timer != null) {
console.log('timer cleaned')
clearTimeout(this.timer)
this.timer = null
}
}
image.onerror = onError.bind(this)
this.timer = setTimeout(() => {
console.log('timer started')
image.onload = function () {}
image.onerror()
image = null
}, this.timeout)
image.src = src
},
cachedOnlineImage() {
if (this.src != "") {
if (this.src.startsWith('http')) {
console.log('http found in link')
let filePath = localStorage.getItem(this.src)
if (filePath != null) {
console.log('localStorage file path is NOT null!')
resolveLocalFileSystemURL(filePath, (file) => {
console.log('local file system url resolved.')
this.testImageAndSetIfSucceed(file.toInternalURL(), () => {
console.log('local file system url is NOT exists, fail returned from testImageAndSetIfSucceed.')
console.log(`Downloading ${this.src}...`)
this.downloadAndCacheImage()
})
}, () => {
console.log('local file system url NOT resolved.')
console.log(`Downloading ${this.src}...`)
this.downloadAndCacheImage()
})
} else {
console.log('localStorage filePath is null.')
console.log(`Downloading ${this.src}...`)
this.downloadAndCacheImage()
}
} else {
console.log(`http not found in url. Testing image: ${this.src}`)
this.testImageAndSetIfSucceed(this.src, () => {
console.log('Image test failed. Setting to fallback image.')
this.img = this.localFallbackImage
})
}
}
}
},
created() {
if (this.placeholder != "")
this.img = this.placeholder
document.addEventListener("deviceready", this.cachedOnlineImage.bind(this))
}
}
</script>
@centrual have you ever looked at the content-sync plugin? Seems to do a lot of what you want.
Hello @macdonst
This plugin is good too, but it's hard to use for dynamic mobile apps.
The code i sent working good for dynamic images. I using it currently, and i just shared for everybody.
@centrual I am very excited by this code you have shared. I really want something to handle offline images but where I can still use simple html tags referencing remote sources such that a remote update forces a change; but otherwise the app should use local images. Do you know if phonegap stores the cached images indefinitely? Please could you give some pointers for setting this up and getting your example up and running. Do you have a working example I could download and run to get my head around what's going on? I struggle with where I should set this up in my Framework7 component/template?
Also, would this work within a virtualList?
Hello Vladimir and Framework7 community;
I trying to cache all images in phone's storage (or localStorage).
Caching easy in vanilla javascript( with some ready to use scripts ) but it's hard in vuejs (especially on lists, and custom components like photo viewer), i thinking how can be easier.
I know it's not framework7's main target, it's another project maybe but;
Maybe we can make
component for cordova environments?
it saves image to [localstorage|sdcard|phone] and displays from there if it can't load from url.
Have a great day!