アプリ固有の外部ストレージに画像ファイルを保存する方法について見てみましょう。
2024.1.1
アプリ固有の外部ストレージ
こちらで アプリ固有の内部ストレージにファイルを保存 を試しましたが、画像ファイルなどの大きなファイルは内部ストレージが足りないので外部ストレージに保存します。
アプリ固有の外部ストレージ:
- アプリで使う画像や動画等、大きなファイルを保存できる
- アプリがアンインストールされると削除される
- 使用可能状態か確認が必要
- アプリ自身からのアクセスPermissionは不要
- 他のアプリも適切な権限を持っていればにアクセスでき、自分のコントロールの及ばない所で読み取られる可能性がある。データ ストレージとファイル ストレージの概要
- アプリがアンインストールされても残った方がいい場合などでは、共有のメディアや他の外部ストレージに保存することも可能です。
getExternalFilesDir
以前は↓を使ってダイレクトのパスを取得できましたが
Environment.getExternalStorageDirectory().getPath()
API29からは使えません。代わりに、
- Context#getExternalFilesDir(String)
これを使ってアプリ固有の外部ストレージに保存するパスを設定します。
1 2 3 4 |
Context context = getApplicationContext(); String fileName = "sample_image.jpg"; File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName); |
「DIRECTORY_PICTURES」で指定しましたが他にも「DIRECTORY_DCIM」などの選択肢はあります。
サンプルコード
保存する画像 sample_image.jpg をassetsに置きます。
画像ファイルをassetsに置いて取り出す方法については以下を参考にしてください。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
//package com.example.testprivateexternalstorage; import android.os.Bundle; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.util.Log; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; public class MainActivity extends AppCompatActivity { private TextView textView; private ImageView imageView; // asset の画像ファイル名 private final String fileName = "sample_image.jpg"; private File file; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); Context context = getApplicationContext(); // 画像を置く外部ストレージ file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName); Log.d("log","path: " + file); textView = findViewById(R.id.text_view); String str = "image file: "+fileName; textView.setText(str); imageView = findViewById(R.id.image_view); setUpWriteExternalStorage(); } private void setUpWriteExternalStorage(){ Button buttonSave = findViewById(R.id.button_save); // 外部ストレージに画像を保存する buttonSave.setOnClickListener( v -> { if(isExternalStorageWritable()){ try(// assetsから画像ファイルを取り出し InputStream inputStream = getResources().getAssets().open(fileName); // 外部ストレージに画像を保存 FileOutputStream output = new FileOutputStream(file)) { // バッファーを使って画像を書き出す int DEFAULT_BUFFER_SIZE = 10240 * 4; byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; int len; while((len=inputStream.read(buf))!=-1){ output.write(buf,0, len); } output.flush(); textView.setText(R.string.saved); } catch (IOException e) { throw new RuntimeException(e); } } }); Button buttonRead = findViewById(R.id.button_read); buttonRead.setOnClickListener( v -> { if(isExternalStorageReadable()){ try(InputStream inputStream0 = Files.newInputStream(file.toPath())) { Bitmap bitmap = BitmapFactory.decodeStream(inputStream0); imageView.setImageBitmap(bitmap); } catch (IOException e) { throw new RuntimeException(e); } } }); } /* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); return (Environment.MEDIA_MOUNTED.equals(state)); } /* Checks if external storage is available to at least read */ public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); return (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)); } } |
その他、レイアウト等のファイルは以下に
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/text_view" android:textSize="24sp" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.3" /> <ImageView android:id="@+id/image_view" android:scaleType="fitCenter" android:layout_width="match_parent" android:layout_height="wrap_content" android:contentDescription="@string/description" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.2" /> <Button android:id="@+id/button_save" android:text="@string/save" android:layout_width="300dp" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.7" /> <Button android:id="@+id/button_read" android:text="@string/read" android:layout_width="300dp" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.8" /> </androidx.constraintlayout.widget.ConstraintLayout> |
リソースです
strings.xml
1 2 3 4 5 6 7 |
<resources> <string name="app_name">TestPrivateExternalStorage</string> <string name="description">photo</string> <string name="save">Button Save</string> <string name="read">Button Read</string> <string name="saved">image saved</string> </resources> |
これで画像を保存して読み出してみましょう。
保存された画像はどこにあるか
注)あくまでデバッグ段階でのファイル確認です
画像はどこに保存されているのでしょうか、アプリ固有の外部ストレージなので「フォト」を見ても画像はありません。
logcatにパスを出力させていたので、
1 |
path: /storage/emulated/0/Android/data/com.example.testprivateexternalstorage/files/Pictures/sample_image.jpg |
実機で確認するとパスは、
1 |
path: /storage/emulated/0/Android/data/com.example.testprivateexternalstorage/files/Pictures/sample_image.jpg |
となっているのでターミナルで入ってみます。
sample_image.jpgがあることが確認できます。
Device Explorerで見ると、画像を確認できました
関連記事:
References:
ファイルを保存する
ストレージ オプション | Android Developers