'use strict';
const _ = require('lodash');
const RNFS = require('react-native-fs');
const {
DocumentDirectoryPath
} = RNFS;
const SHA1 = require("crypto-js/sha1");
const URL = require('url-parse');
const defaultOptions = {
useQueryParamsInCacheKey: false
};
const activeDownloads = {};
const BaseDirPath = 'baseDirPath';
function getBasePath(){
return DocumentDirectoryPath + '/' + BaseDirPath;
}
function serializeObjectKeys(obj) {
return _(obj)
.toPairs()
.sortBy(a => a[0])
.map(a => a[1])
.value();
}
function getQueryForCacheKey(url, useQueryParamsInCacheKey) {
if (_.isArray(useQueryParamsInCacheKey)) {
return serializeObjectKeys(_.pick(url.query, useQueryParamsInCacheKey));
}
if (useQueryParamsInCacheKey) {
return serializeObjectKeys(url.query);
}
return '';
}
function generateCacheKey(url, options) {
const parsedUrl = new URL(url, null, true);
const pathParts = parsedUrl.pathname.split('/');
// last path part is the file name
const fileName = pathParts.pop();
const filePath = pathParts.join('/');
const parts = fileName.split('.');
// TODO - try to figure out the file type or let the user provide it, for now use jpg as default
const type = parts.length > 1 ? parts.pop() : 'jpg';
const cacheable = filePath + fileName + type + getQueryForCacheKey(parsedUrl, options.useQueryParamsInCacheKey);
return SHA1(cacheable) + '.' + type;
}
function getCachePath(url, options) {
if (options.cacheGroup) {
return options.cacheGroup;
}
const parsedUrl = new URL(url);
return parsedUrl.host;
}
function getCachedImageFilePath(url, options) {
const cacheKey = generateCacheKey(url, options);
const cachePath = getCachePath(url, options);
const dirPath = getBasePath() + '/' + cachePath;
return dirPath + '/' + cacheKey;
}
function deleteFile(filePath) {
return RNFS.exists(filePath)
.then(res => res && RNFS.unlink(filePath))
.catch((err) => {
// swallow error to always resolve
});
}
function ensurePath(filePath) {
const parts = filePath.split('/');
const dirPath = _.initial(parts).join('/');
return RNFS.mkdir(dirPath, {NSURLIsExcludedFromBackupKey: true});
}
/**
* returns a promise that is resolved when the download of the requested file
* is complete and the file is saved.
* if the download fails, or was stopped the partial file is deleted, and the
* promise is rejected
* @param fromUrl
* @param toFile
* @returns {Promise}
*/
function downloadImage(fromUrl, toFile) {
// use toFile as the key as is was created using the cacheKey
if (!_.has(activeDownloads, toFile)) {
// create an active download for this file
activeDownloads[toFile] = new Promise((resolve, reject) => {
const downloadOptions = {
fromUrl,
toFile
};
RNFS.downloadFile(downloadOptions).promise
.then(res => {
if (Math.floor(res.statusCode / 100) == 2) {
resolve(toFile);
} else {
return Promise.reject('Failed to successfully download image')
}
})
.catch(err => {
return deleteFile(toFile)
.then(() => reject(err))
})
.finally(() => {
// cleanup
delete activeDownloads[toFile];
});
});
}
return activeDownloads[toFile];
}
function createPrefetcer(list) {
const urls = _.clone(list);
return {
next() {
return urls.shift();
}
};
}
function runPrefetchTask(prefetcher, options) {
const url = prefetcher.next();
if (!url) {
return Promise.resolve();
}
// if url is cacheable - cache it
if (isCacheable(url)) {
// check cache
return getCachedImagePath(url, options)
// if not found download
.catch(() => cacheImage(url, options))
// then run next task
.then(() => runPrefetchTask(prefetcher, options));
}
// else get next
return runPrefetchTask(prefetcher, options);
}
// API
function isCacheable(url) {
return _.isString(url) && (_.startsWith(url, 'http://') || _.startsWith(url, 'https://'));
}
function getCachedImagePath(url, options = defaultOptions) {
const filePath = getCachedImageFilePath(url, options);
return RNFS.stat(filePath)
.then(res => {
if (!res.isFile()) {
// reject the promise if res is not a file
throw new Error('Failed to get image from cache');
}
if (!res.size) {
// something went wrong with the download, file size is 0, remove it
return deleteFile(filePath)
.then(() => {
throw new Error('Failed to get image from cache');
});
}
return filePath;
})
.catch(err => {
throw err;
})
}
function cacheImage(url, options = defaultOptions) {
const filePath = getCachedImageFilePath(url, options);
return ensurePath(filePath)
.then(() => downloadImage(url, filePath));
}
function deleteCachedImage(url, options = defaultOptions) {
const filePath = getCachedImageFilePath(url, options);
return deleteFile(filePath);
}
function cacheMultipleImages(urls, options = defaultOptions) {
const prefetcher = createPrefetcer(urls);
const numberOfWorkers = urls.length;
const promises = _.times(numberOfWorkers, () =>
runPrefetchTask(prefetcher, options)
);
return Promise.all(promises);
}
function deleteMultipleCachedImages(urls, options = defaultOptions) {
return _.reduce(urls, (p, url) =>
p.then(() => deleteCachedImage(url, options)),
Promise.resolve()
);
}
function clearCache() {
deleteFile(getBasePath());
ensurePath(getBasePath());
}
function getStat(path) {
return RNFS.stat(path)
.then((res) => {
if (res) {
return {
...res,
path: path
};
}
return null;
}, (error) => {
return 0;
})
}
function getDirSize(path) {
return new Promise((resolve, reject) => {
let total = 0;
RNFS.readdir(path)
.then((subItems) => {
const statArr = [];
const funcArr = [];
if (!subItems || subItems.length <= 0) {
resolve(0);
return;
}
subItems.forEach((itemPath) => {
statArr.push(getStat(path + '/' + itemPath))
});
Promise.all(statArr)
.then((arr) => {
if (!arr || arr.length <= 0) {
resolve(0);
return;
}
arr.forEach((item) => {
if (item) {
if (item.isFile()) {
total += item.size;
} else {
funcArr.push(getDirSize(item.path));
}
}
});
if (funcArr.length > 0) {
Promise.all(funcArr)
.then((sizeList) => {
total += sizeList.reduce((a, b) => {
return a + b;
});
resolve(total);
});
} else {
resolve(total);
}
});
}, (error) => {
resolve(0);
});
});
}
function getCacheSize() {
return getDirSize(getBasePath());
}
module.exports = {
isCacheable,
getCachedImagePath,
cacheImage,
deleteCachedImage,
cacheMultipleImages,
deleteMultipleCachedImages,
clearCache,
getCacheSize
};
image cache provider
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門缓淹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人塔逃,你說我怎么就攤上這事讯壶。” “怎么了湾盗?”我有些...
- 文/不壞的土叔 我叫張陵伏蚊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我格粪,道長(zhǎng)躏吊,這世上最難降的妖魔是什么氛改? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮比伏,結(jié)果婚禮上胜卤,老公的妹妹穿的比我還像新娘。我一直安慰自己凳怨,他們只是感情好瑰艘,可當(dāng)我...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肤舞,像睡著了一般紫新。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上李剖,一...
- 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宰僧!你這毒婦竟也來了材彪?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬榮一對(duì)情侶失蹤琴儿,失蹤者是張志新(化名)和其女友劉穎段化,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體造成,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡显熏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晒屎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喘蟆。...
- 正文 年R本政府宣布尺棋,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏膘螟。R本人自食惡果不足惜成福,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荆残。 院中可真熱鬧奴艾,春花似錦、人聲如沸内斯。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)俘闯。三九已至潭苞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間真朗,已是汗流浹背此疹。 一陣腳步聲響...
- 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像旗扑,于是被迫代替她去往敵國(guó)和親蹦骑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 介紹 OpenStack使用分布式存儲(chǔ)Ceph后臀防,就不需要Glance的cache image了眠菇,我們可以通過修改...
- 介紹 OpenStack使用Ceph分布式存儲(chǔ)后,理論上不需要cache image了清钥,在部署時(shí)可以修改這個(gè)配置琼锋,...
- 標(biāo)簽: cinder openstack 翻譯 原文: Image-Volume cache Openstack塊...
- 里約起蒼黃放闺, 奧運(yùn)大戰(zhàn)忙祟昭, 女排奪冠尤可嘉, 無敵靠自強(qiáng)怖侦。 郎導(dǎo)傳精神篡悟, 布陣調(diào)度詳, 長(zhǎng)傳短吊攔扣狠匾寝, 個(gè)個(gè)賽虎...