🖼️

【Android】URL から画像をダウンロードする

2023/02/27に公開

URL から画像をダウンロードする機能を実装する機会があったので、試した方法について書いてみたいと思います。

Picasso

build.gradle

implementation 'com.squareup.picasso:picasso:2.71828'

実装

fun downloadImage(url: URL): Bitmap {
    return Picasso.get()
        .load(url.toString())
        .get()
}

Glide

build.gradle

def glideVersion = '4.15.0'
implementation "com.github.bumptech.glide:glide:$glideVersion"
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"

実装

fun downloadImage(url: URL): Bitmap {
    return Glide.with(context)
        .asBitmap()
        .load(url.toString())
        .submit()
        .get()
}

openConnection

実装

fun downloadImage(url: URL): Bitmap {
    return url.openConnection().getInputStream().use { stream ->
        BitmapFactory.decodeStream(stream)
    }
}

実装例 : 画像をダウンロードして Picutures フォルダに保存する

ImageDownload.kt

class ImageDownload {

    fun download(
        resolver: ContentResolver,
        url: URL,
        directory: String
    ): Boolean {

        // 画像を保存するURIを取得
        val values = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, getFileName(url))
            put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
            put(
                MediaStore.Images.Media.DATE_MODIFIED,
                Instant.ofEpochMilli(Date().time).epochSecond
            )
        }
        val contentUri = getContentUri(resolver, values, url, directory) ?: return false
	
	// 画像をダウンロード
        val bitmap = url.openConnection().getInputStream().use { stream ->
            BitmapFactory.decodeStream(stream)
        }

        // 画像を書き込む
        val isSuccess = resolver.openOutputStream(contentUri, "w").use { stream ->
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
        }

        // Android10 以上の場合は、排他制御を解除する
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.IS_PENDING, Pending.DISABLED.value)
            resolver.update(contentUri, values, null, null)
        }

        return isSuccess
    }

    private fun getContentUri(
        resolver: ContentResolver,
        values: ContentValues,
        url: URL,
        directory: String
    ) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        getUriApiVersionQ(resolver, values, directory)
    } else {
        getUri(resolver, url, values, directory)
    }

    private fun getUri(
        resolver: ContentResolver,
        url: URL,
        values: ContentValues,
        directory: String
    ): Uri? {
        val file = File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            directory
        )
        if (!file.exists()) {
            file.mkdir()
        }
        values.put(
	    MediaStore.Images.Media.DATA, 
	    File(file, getFileName(url)).absolutePath
	)
        return resolver.insert(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            values
        )
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    private fun getUriApiVersionQ(
        resolver: ContentResolver,
        values: ContentValues,
        directory: String
    ): Uri? {
        values.apply {
            put(
                MediaStore.Images.Media.RELATIVE_PATH,
                File(Environment.DIRECTORY_PICTURES, directory).path
            )
            put(MediaStore.Images.Media.IS_PENDING, Pending.ENABLED.value)
        }
        return resolver.insert(
            MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
            values
        )
    }

    private fun getFileName(url: URL): String {
        val index = url.file.lastIndexOf("/")
        return url.file.substring(index + 1)
    }
    
    private enum class Pending(val value: Int) {
        ENABLED(1),
        DISABLED(0)
    }
}

Discussion