react-native-fast-image缓存动态url图片

911 阅读2分钟

需求:
app缓存阿里云OSS授权的url

基于react-native-fast-image改动方案:

  1. 先下载图片到本地磁盘,再通过FastImage展示
  2. 为单独的FastImage设置缓存key
  3. 为所有的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} />

patch地址