Glide 如何加载远程 Base64 图片
最近有个需求,后端给出的图片地址并不是正常的 URL,而且需要一个接口去请求,但是返回的是 base64 数据流。这里不关心为啥要这么多,原因有很多,可能是系统的问题,也可能是能力问题。当然作为我们 Android 程序员,要紧的是如何解决这个问题。
首先我们拿到接口链接,我这次拿到的是这样的:
https://www.example.com/cdn/attach/{fileId}/base64
这里的{fileId}
是指图片的 id,那么正常的图片地址可以理解为:
https://www.example.com/cdn/attach/1000/base64
https://www.example.com/cdn/attach/1002/base64
熟悉 Glide 加载逻辑的人,应该很熟悉,这种方式可能需要我们自定义ModelLoader
来解决问题,们可以让 Glide 将 API 接口当作一种图片源来处理,就像处理普通的图片 URL 一样。
实现方案
我们可以自定义 Base64ApiModelLoader 来处理 base64 的请求数据:
public class Base64ApiModelLoader implements ModelLoader<String, InputStream> {private static final String BASE_URL = "https://your-api-base-url/";private static final String API_PATH = "cdn/attach/";private final OkHttpClient okHttpClient;public Base64ApiModelLoader() {this.okHttpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(15, TimeUnit.SECONDS).build();}@Nullable@Overridepublic LoadData<InputStream> buildLoadData(@NonNull String fileId, int width, int height, @NonNull Options options) {// 创建缓存键,使用文件ID作为唯一标识Key key = new ObjectKey(API_PATH + fileId);// 返回加载数据,包含缓存键和数据获取器return new LoadData<>(key, new Base64ApiFetcher(fileId, okHttpClient));}@Overridepublic boolean handles(@NonNull String model) {// 判断是否是文件ID格式,这里简单判断不是URLreturn !model.startsWith("http") && !model.startsWith("data:");}// 工厂类,用于创建ModelLoaderpublic static class Factory implements ModelLoaderFactory<String, InputStream> {@NonNull@Overridepublic ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {return new Base64ApiModelLoader();}@Overridepublic void teardown() {// 清理资源}}// 数据获取器,负责从API获取Base64数据并转换为InputStreamprivate static class Base64ApiFetcher implements DataFetcher<InputStream> {private final String fileId;private final OkHttpClient okHttpClient;private InputStream inputStream;private volatile boolean isCancelled;Base64ApiFetcher(String fileId, OkHttpClient okHttpClient) {this.fileId = fileId;this.okHttpClient = okHttpClient;}@Overridepublic void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {if (isCancelled) {callback.onLoadFailed(new IOException("Cancelled"));return;}// 构建API请求URLString apiUrl = BASE_URL + API_PATH + fileId + "/base64";Request request = new Request.Builder().url(apiUrl).build();try {// 执行请求Response response = okHttpClient.newCall(request).execute();if (!response.isSuccessful()) {callback.onLoadFailed(new IOException("Failed to load Base64 data: " + response.code()));return;}// 解析响应体ResponseBody responseBody = response.body();if (responseBody == null) {callback.onLoadFailed(new IOException("Empty response"));return;}// 解析JSON响应String jsonString = responseBody.string();JSONObject jsonObject = new JSONObject(jsonString);// 检查响应码int code = jsonObject.optInt("code", -1);if (code != 200) {callback.onLoadFailed(new IOException("API error: " + jsonObject.optString("message", "Unknown error")));return;}// 获取Base64数据String base64Data = jsonObject.optString("data", "");if (base64Data.isEmpty()) {callback.onLoadFailed(new IOException("Empty Base64 data"));return;}// 处理可能存在的Data URI前缀if (base64Data.contains(",")) {base64Data = base64Data.split(",")[1];}// 解码Base64数据byte[] imageBytes = Base64.decode(base64Data, Base64.DEFAULT);// 创建输入流inputStream = new ByteArrayInputStream(imageBytes);// 回调成功callback.onDataReady(inputStream);} catch (IOException | JSONException | IllegalArgumentException e) {if (!isCancelled) {callback.onLoadFailed(e);}}}@Overridepublic void cleanup() {if (inputStream != null) {try {inputStream.close();} catch (IOException ignored) {// 忽略关闭错误}}}@Overridepublic void cancel() {isCancelled = true;}@NonNull@Overridepublic Class<InputStream> getDataClass() {return InputStream.class;}@NonNull@Overridepublic DataSource getDataSource() {return DataSource.REMOTE;}}
}
定义完成ModelLoader
之后,我们可能就要注册ModelLoader
了。
@GlideModule
public class MyAppGlideModule extends AppGlideModule {@Overridepublic void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {super.registerComponents(context, glide, registry);// 注册我们的自定义ModelLoader,用于处理Base64 API请求registry.append(String.class, InputStream.class, new Base64ApiModelLoader.Factory());}@Overridepublic boolean isManifestParsingEnabled() {return false;}
}
当然不能忘记需要有注解处理器
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
处理完之后,我们就可以这么调用了:
Glide.with(imageView.getContext()).load(fileId) // +.placeholder(R.mipmap.default_image).error(R.mipmap.default_image).into(imageView);
我们之前 load 方法中一直调用的是 url
, 这里就直接调用 fileId
即可。因为我们已经定义了
registry.append(String.class, InputStream.class, new Base64ApiModelLoader.Factory());
其他地方都不变,即可正常进行请求了。