LoginSignup
0
0

More than 5 years have passed since last update.

[Android] [Java] Glide を使って GCS (Google Cloud Storage) 上の画像を Stream でダウンロードする

Last updated at Posted at 2018-11-11

Glide

Glide は Android 用の画像読み込み & キャッシュライブラリです.
https://github.com/bumptech/glide

Gradle であれば以下のようにして導入できます.

build.gradle
repositories {
    mavenCentral()
    google()
}

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.8.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
}

準備

独自モデル

GCS 上に存在するファイルを Bucket と PATH で指定する GcsImage というクラスを定義します.

GcsImage.java
public class GcsImage {

    private final String mBucket;
    private final String mFilename;

    public GcsImage(@NonNull String bucket, @NonNull String filename) {
        mBucket = bucket;
        mFilename = filename;
    }

    public String getBucket() { return mBucket; }

    public String getFilename() { return mFilename; }
}

Glide Module

下記リンクに従って,先ほどの GcsImage を読み込むための ModelLoader を定義します.
Writing a custom ModelLoader

The first step is to implement the ModelLoader interface. Before we do so, we need to make two decisions:

  1. What type of Model should we handle?
  2. What type of Data should we produce for that Model?

とあるように,ModelLoader では,扱う Model と Data 型を指定します.

Model は String 等でも良いのですが,今回は独自の GcsImage を使用します.
Data 型としては,標準で InputStreamByteBuffer のデコーダが用意されています.

GcsImageLoader.java
public class GcsImageLoader implements ModelLoader<GcsImage, InputStream> {

    private Context mContext;

    public GcsImageLoader(Context context) {
        mContext = context;
    }

    @Nullable
    @Override
    public LoadData<InputStream> buildLoadData(@NonNull GcsImage gcsImage, int width, int height, @NonNull Options options) {
        // バケット名とファイル名が同じであればキャッシュデータを利用するようにします.
        return new LoadData<>(new ObjectKey(gcsImage.getBucket() + '/' + gcsImage.getFilename()), new GcsImageFetcher(gcsImage));
    }

    @Override
    public boolean handles(@NonNull GcsImage gcsImage) {
        return true;
    }

    private class GcsImageFetcher implements DataFetcher<InputStream> {
        private GcsImage gcsImage;

        GcsImageFetcher(GcsImage gcsImage) {
           this.gcsImage = gcsImage;
        }

        @Override
        public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
            try {
                // 実際の読み込み処理を記述します. ここでは独自のクラスを実装しました.
                InputStream stream = CloudStorageUtils.downloadToStream(mContext, gcsImage.getBucket(), gcsImage.getFilename());
                callback.onDataReady(stream);
            } catch (IOException e) {
                callback.onLoadFailed(e);
            }
        }

        @Override
        public void cleanup() {}

        @Override
        public void cancel() {}

        @NonNull
        @Override
        public Class<InputStream> getDataClass() {
            return InputStream.class;
        }

        @NonNull
        @Override
        public DataSource getDataSource() {
            return DataSource.REMOTE;
        }
    }

    public static class GcsImageLoaderFactory implements ModelLoaderFactory<GcsImage, InputStream> {
        private Context context;

        public GcsImageLoaderFactory(Context context) {
            this.context = context;
        }

        @NonNull
        @Override
        public ModelLoader<GcsImage, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
            return new GcsImageLoader(context);
        }

        @Override
        public void teardown() {}
    }
}

CloudStorageUtils.downloadToStream() というメソッドも独自に作成したもので,以下のようなクラスに定義しました.

CloudStorageUtils.java
public class CloudStorageUtils {

    @WorkerThread
    public static InputStream downloadToStream(@NonNull Context context, @NonNull String bucket,
                                               @NonNull String name) throws IOException {
        Storage storage = getStorageService(context);
        Storage.Objects.Get get = storage.objects().get(bucket, name);
        return get.executeMediaAsInputStream();
    }

    @WorkerThread
    @NonNull
    public static Storage getStorageService(@NonNull Context context) throws IOException {
        HttpTransport transport = new NetHttpTransport();
        JsonFactory jsonFactory = new JacksonFactory();
        // 認証情報の取得部分も別クラスで定義しています.
        Credential credential = GoogleApiCredentialFactory.getCredential(context);
        return new Storage(transport, jsonFactory, credential);
    }
}
GoogleApiCredentialFactory.java
public class GoogleApiCredentialFactory {

    private static Credential sCredential;

    public static synchronized Credential getCredential(@NonNull Context context) {
        if (sCredential == null) {
            // JSON ファイルを Stream で読み込み, Credential を取得します.
            try (InputStream is = context.getAssets().open(BuildConfig.GCP_CREDENTIAL)) {
                List<String> scopes = new ArrayList<>();
                scopes.add(StorageScopes.DEVSTORAGE_FULL_CONTROL);
                GoogleCredential credential = GoogleCredential.fromStream(is);
                sCredential = credential.createScoped(scopes);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return sCredential;
    }
}

BuildConfig.GCP_CREDENTIAL には,GCP のサービスアカウントキーが記載された JSON ファイルへのパスを指定します.

では,作成した GcsImageLoader を追加しましょう.

GcsImageGlideModule.java
@GlideModule
public class GcsImageGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        registry.prepend(GcsImage.class, InputStream.class, new GcsImageLoader.GcsImageLoaderFactory(context));
    }
}

@GlideModule というアノテーションをつけることにより,Glide は AppGlideModule を継承した GcsImageGlideModule を認識してくれます.

前述の通り,build.gradle には Annotation Processor の記述が必要です.

参照: Module classes and annotations.

実装

自作した GlideModule を使って GCS 上の画像を読み込むプログラムは以下の通りです.
ここでは mImageView という ImageView に画像を表示させています.

Glide.with(this).load(new GcsImage(bucket, filename)).into(mImageView);

以上で,無事画像を表示することができました.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0