需求:
app缓存阿里云OSS授权的url
基于react-native-fast-image改动方案:
- 先下载图片到本地磁盘,再通过FastImage展示
- 为单独的FastImage设置缓存key
- 为所有的FastImage设置缓存key的生成规则
方案1: 不用改的FastImage库,但是FastImage磁盘缓存地址不会用输入的下载磁盘地址,而是重新也存一份,需要考虑空间问题,另外需要维护图片url和下载磁盘地址关系
方案2: 需要修改FastImage库,只影响当前请求图片的cacheKey,需要在js中计算cacheKey,判断cache是否存储
方案3: 需要修改FastImage库,会影响所有的Cachekey的生成,启动的时候配置CacheKey的生成规则
最终选择了方案3,FastImage对应的原生库都提供了自定义CacheKey的生成,所以问题不大。
OSS授权url方式选择
OSS签名授权提供了2种方式:
- 签名和其他参数都拼在url上
- 签名和其他参数放在header
最终选择第一种方式,主要是查看直接,而且第二种方式无法设置过期时间,后续可能有这种需求
代码实现
- 自定义CacheKey生成规则关键代码
iOS:
RCT_EXPORT_METHOD(setupCacheKeyStrategy:(nonnull FFFCacheKeyStrategy *)cacheKeyStrategy)
{
if (cacheKeyStrategy.cacheKeyType == FFFCacheKeyIgnoreQueryParam) {
NSArray *includeKeys = [NSArray arrayWithArray:cacheKeyStrategy.includeKeys];
[SDWebImageManager sharedManager].cacheKeyFilter =[SDWebImageCacheKeyFilter cacheKeyFilterWithBlock:^NSString * _Nullable(NSURL * _Nonnull url) {
NSString *query = url.query;
if (!query || query.length == 0) {
return [url absoluteString];
}
NSMutableString *ret = [NSMutableString stringWithString:[[url absoluteString] componentsSeparatedByString:@"?"][0]];
if ([includeKeys count] > 0) {
if (query && query.length > 0) {
BOOL hasAddQueryParam = NO;
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
for (NSURLQueryItem *item in components.queryItems) {
if ([includeKeys containsObject:item.name]) {
if (!hasAddQueryParam) {
[ret appendString:@"?"];
[ret appendFormat:@"%@=%@", item.name, item.value];
hasAddQueryParam = YES;
} else {
[ret appendFormat:@"&%@=%@", item.name, item.value];
}
}
}
}
}
return ret;
}];
}
}
Android:
public class FastImageGlideUrl extends GlideUrl {
public static FFFCacheKeyStrategy cacheKeyStrategy = null;
private String mUrl = "";
public FastImageGlideUrl(String url, Headers headers) {
super(url, headers);
this.mUrl = url;
}
@Override
public String getCacheKey() {
if (cacheKeyStrategy == null || cacheKeyStrategy.mCacheKeyType == FFFCacheKeyType.Normal) {
return super.getCacheKey();
}
if (cacheKeyStrategy.mCacheKeyType == FFFCacheKeyType.IgnoreQueryParam) {
if (mUrl != null && mUrl.contains("?")) {
if (cacheKeyStrategy.mExcludeKeys != null) {
String ret = mUrl.split("\\?")[0];
Uri uri = Uri.parse(String.valueOf(mUrl));
boolean hasAddQueryParam = false;
for (int i = 0; i < cacheKeyStrategy.mExcludeKeys.size(); i++) {
String k = (String) cacheKeyStrategy.mExcludeKeys.get(i);
String v = uri.getQueryParameter(k);
if (v != null) {
if (!hasAddQueryParam) {
ret = ret + "?" + k + "=" + v;
hasAddQueryParam = true;
} else {
ret = ret + "&" + k + "=" + v;
}
}
}
return ret;
}
}
}
return super.getCacheKey();
}
}
- 检查url缓存是否存在 iOS
RCT_EXPORT_METHOD(existCache:(nonnull FFFastImageSource *)source
withResolver:(RCTPromiseResolveBlock)resolve
andRejecter:(RCTPromiseRejectBlock)reject)
{
if (!source || !source.url) {
resolve([NSNull null]);
}
NSString *cacheKey = [[SDWebImageManager sharedManager] cacheKeyForURL:source.url];
BOOL isCached = [[SDImageCache sharedImageCache] diskImageDataExistsWithKey:cacheKey];
if (isCached) {
resolve(@(isCached));
} else {
resolve([NSNull null]);
}
}
Android:
@ReactMethod
public void existCache(final ReadableMap source, final Promise promise) {
final Activity activity = getCurrentActivity();
if (activity == null) {
promise.resolve(null);
return;
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
final FastImageSource imageSource = FastImageViewConverter.getImageSource(activity, source);
final FastImageGlideUrl glideUrl = imageSource.getGlideUrl();
if (glideUrl == null) {
promise.resolve(null);
return;
}
Glide
.with(activity.getApplicationContext())
.asFile()
.load(glideUrl)
.apply(FastImageViewConverter.getOptions(activity, imageSource, source))
.listener(new RequestListener<File>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> target, boolean isFirstResource) {
promise.reject(ERROR_LOAD_FAILED, e);
return false;
}
@Override
public boolean onResourceReady(File resource, Object model, Target<File> target, DataSource dataSource, boolean isFirstResource) {
promise.resolve(true);
return false;
}
})
.submit();
}
});
}
js实现:
// 启动时
FastImage.setupCacheKeyStrategy({
cacheKeyType: 'ignoreQueryParam',
includeKeys: ['process', 'x-oss-process'],
});
// 使用FastImage
const uri = "xxx"
const existCache = await FastImage.existCache(uri)
if (existCache) {
setSource({uri: uri})
} else {
// 请求服务器获取到OSS签名url
const signedUrl = "xxx"
setSource({uri: signedUrl})
}
<FastImage source={source} />