Android에서 외부 SD 카드에 쓰는 보편적 인 방법
내 응용 프로그램에서 장치 저장소에 많은 이미지를 저장해야합니다. 이러한 파일은 장치 저장 공간을 차지하는 경향이 있으며 사용자가 외부 SD 카드를 대상 폴더로 선택할 수 있도록하고 싶습니다.
나는 Android가 사용자가 외부 SD 카드에 쓸 수 없도록 모든 곳에서 읽었습니다 .SD 카드 는 외부 저장소가 아닌 외부 및 마운트 가능한 SD 카드를 의미 하지만 파일 관리자 응용 프로그램은 모든 Android 버전에서 외부 SD에 쓸 수 있습니다.
다양한 API 레벨 (Pre-KitKat, KitKat, Lollipop +)에서 외부 SD 카드에 대한 읽기 / 쓰기 액세스 권한을 부여하는 더 좋은 방법은 무엇입니까?
업데이트 1
Doomknight의 답변에서 방법 1을 시도했지만 아무 소용이 없습니다. 보시다시피 SD에 쓰기를 시도하기 전에 런타임에 권한을 확인하고 있습니다.
HashSet<String> extDirs = getStorageDirectories();
for(String dir: extDirs) {
Log.e("SD",dir);
File f = new File(new File(dir),"TEST.TXT");
try {
if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) {
f.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
그러나 액세스 오류가 발생하여 HTC10 및 Shield K1의 두 가지 장치에서 시도했습니다.
10-22 14:52:57.329 30280-30280/? E/SD: /mnt/media_rw/F38E-14F8
10-22 14:52:57.329 30280-30280/? W/System.err: java.io.IOException: open failed: EACCES (Permission denied)
10-22 14:52:57.329 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:939)
10-22 14:52:57.329 30280-30280/? W/System.err: at com.myapp.activities.TestActivity.onResume(TestActivity.java:167)
10-22 14:52:57.329 30280-30280/? W/System.err: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1326)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.Activity.performResume(Activity.java:6338)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2574)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.access$900(ActivityThread.java:150)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1399)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Looper.loop(Looper.java:168)
10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5885)
10-22 14:52:57.330 30280-30280/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:819)
10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709)
10-22 14:52:57.330 30280-30280/? W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.Posix.open(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
10-22 14:52:57.330 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:932)
10-22 14:52:57.330 30280-30280/? W/System.err: ... 14 more
요약
다양한 API 수준 ( 런타임시 API23 +)에서 외부 SD 카드에 대한 읽기 / 쓰기 액세스 권한 을 부여 할 수 있습니다 .
KitKat 이후로 앱별 디렉토리를 사용 하는 경우 권한이 필요하지 않으며 그렇지 않으면 필요 합니다.
보편적 인 방법 :
역사는 이 아니라고 말한다 외부 SD 카드에 쓸 수있는 보편적 인 방법은 있지만, 계속 ...
이 사실 은 장치에 대한 외부 저장 구성의 예를 통해 입증 됩니다 .
API 기반 방식 :
KitKat 이전에 Doomsknight 방법 1 을 사용하고 그렇지 않으면 방법 2 를 사용 하십시오.
매니페스트 (Api <23) 및 런타임 (Api> = 23) 에서 권한 을 요청 합니다.
권장 방법 :
ContextCompat.getExternalFilesDirs 는 파일을 공유 할 필요가 없을 때 액세스 오류를 해결 합니다.
이를 공유하는 안전한 방법은 컨텐츠 제공자 또는 새로운 스토리지 액세스 프레임 워크 를 사용하는 것입니다 .
개인 정보 인식 방법 :
Android Q 베타 4 부터 Android 9 (API 레벨 28) 이하를 대상으로하는 앱은 기본적으로 변경 사항이 없습니다.
기본적으로 Android Q를 타겟팅하는 (또는 선택하는) 앱에는 외부 저장소에 대한 필터링 된보기 가 제공 됩니다.
1. 초기 답변.
Android에서 외부 SD 카드에 쓰는 보편적 인 방법
더 없다 보편적 인 방법으로 하는 안드로이드에 외부 SD 카드에 쓰기 로 인해 지속적인 변화 :
Pre-KitKat : 공식 Android 플랫폼은 예외를 제외하고는 SD 카드를 전혀 지원하지 않습니다.
KitKat : 앱이 SD 카드의 앱별 디렉토리에있는 파일에 액세스 할 수 있도록하는 API를 도입했습니다.
Lollipop : 앱이 다른 공급자가 소유 한 폴더에 대한 액세스를 요청할 수 있도록 API가 추가되었습니다.
Nougat : 일반적인 외부 저장소 디렉토리에 액세스 할 수있는 단순화 된 API를 제공했습니다.
다른 API 수준에서 외부 SD 카드에 대한 읽기 / 쓰기 액세스 권한을 부여하는 더 좋은 방법은 무엇입니까?
를 기반으로 Doomsknight의 대답 과 광산 , 그리고 데이브 스미스 와 1 : 마크 머피 블로그 게시물 , 2 , 3 :
- 이상적으로 는 Jared Rummler가 지적한 대로 Storage Access Framework 및 DocumentFile 을 사용하십시오 . 또는:
- 앱 특정 경로를 사용 합니다
/storage/extSdCard/Android/data/com.myapp.example/files
. - pre-KitKat의 매니페스트 에 읽기 / 쓰기 권한을 추가 합니다. 나중에이 경로에 대한 권한이 필요하지 않습니다 .
- KitKat 및 Samsung 케이스를 고려 하여 앱 경로와 Doomsknight의 방법 을 사용하십시오 .
- KitKat 이전에 getStorageDirectories , 앱 경로 및 읽기 / 쓰기 권한을 필터링하고 사용 합니다.
- KitKat 이후의 ContextCompat.getExternalFilesDirs . 내부를 먼저 반환하는 장치를 고려 합니다.
2. 업데이트 된 답변.
업데이트 1 . Doomknight의 답변에서 방법 1을 시도했지만 아무 소용이 없습니다.
보시다시피 SD에 쓰기를 시도하기 전에 런타임에 권한을 확인하고 있습니다.
업데이트 된 질문 의 문제를 피하고 getExternalFilesDir 설명서 를 참조로 ContextCompat.getExternalFilesDirs()
사용 하기 위해 응용 프로그램 별 디렉토리 를 사용 합니다.
휴리스틱 을 개선하여 다음 과 같은 다양한 API 수준을 기반으로 이동식 미디어를 나타내는 항목을 결정합니다.android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT
...하지만 액세스 오류가 발생하여 HTC10 및 Shield K1의 두 가지 장치에서 시도했습니다.
기억 안드로이드 6.0 지원 휴대용 저장 장치 및 타사 응용 프로그램이 통과해야 저장소 액세스 프레임 워크 . 장치 HTC10 및 Shield K1 은 아마도 API 23 일 것입니다.
로그 에 다음 API 19+에 대한 수정 과 같이 액세스 거부 예외가 표시됩니다 ./mnt/media_rw
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" /> // this line is added via root in the link to fix it.
</permission>
나는 그것을 시도하지 않았으므로 코드를 공유 할 수는 없지만 for
반환 된 모든 디렉토리에 쓰려 는 시도를 피하고 남은 공간을 기반으로 쓸 수있는 최상의 스토리지 디렉토리를 찾습니다 .
아마도 Gizm0의 다른 사용자에 대한 getStorageDirectories()
방법은 좋은 시작점입니다.
ContextCompat.getExternalFilesDirs
다른 폴더에 액세스 할 필요가없는 경우 문제를 해결 합니다 .
3. Android 1.0 .. Pre-KitKat.
KitKat 이전에 Doomsknight 방법 1 을 사용 하거나 Gnathonic 의이 응답 을 읽으십시오.
public static HashSet<String> getExternalMounts() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
// parse output
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase(Locale.US).contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase(Locale.US).contains("vold"))
out.add(part);
}
}
}
}
return out;
}
다음 코드를 추가하고 외부 저장소에 액세스하기를AndroidManifest.xml
읽어 보세요.
외부 저장소에 대한 액세스는 다양한 Android 권한으로 보호됩니다.
안드로이드 1.0부터, 쓰기 액세스는 보호되어
WRITE_EXTERNAL_STORAGE
허가 .Android 4.1부터 읽기 액세스는 권한으로 보호됩니다
READ_EXTERNAL_STORAGE
.... 외부 저장소에 파일을 쓰려면 앱이 ... 시스템 권한을 획득해야합니다.
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>
둘 다 필요한 경우 ...
WRITE_EXTERNAL_STORAGE
권한 만 요청하면됩니다 .
읽기 마크 머피의 설명 및 권장 다이앤 Hackborn 와 데이브 스미스 게시물
- Android 4.4까지는 Android에서 이동식 미디어에 대한 공식적인 지원이 없었습니다. KitKat부터 FMW API에서 "기본"및 "보조"외부 저장소 개념이 등장했습니다.
- 이전 앱은 MediaStore 인덱싱에 의존하고 하드웨어와 함께 제공하거나 마운트 지점을 검사하고 몇 가지 휴리스틱을 적용하여 이동식 미디어를 나타내는 것이 무엇인지 확인했습니다.
4. Android 4.4 KitKat에는 SAF (Storage Access Framework)가 도입되었습니다 .
버그로 인해 다음 메모를 무시하고 다음을 사용하십시오 ContextCompat.getExternalFilesDirs()
.
- Android 4.2 이후 Google은 기기 제조업체에서 보안 (다중 사용자 지원)을 위해 이동식 미디어를 잠그도록 요청했으며 4.4에서 새로운 테스트가 추가되었습니다.
getExternalFilesDirs()
사용 가능한 모든 스토리지 볼륨에서 사용 가능한 경로를 반환하기 위해 KitKat 및 기타 메서드가 추가 되었으므로 반환 된 첫 번째 항목은 기본 볼륨입니다.- 아래 표는 개발자가 시도 할 수있는 작업과 KitKat이 응답하는 방식을 나타냅니다.
참고 : Android 4.4부터 앱에 비공개 인 파일 만 읽거나 쓰는 경우 이러한 권한이 필요하지 않습니다. 자세한 내용 은 app-private 파일 저장을 참조하세요 .
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" /> </manifest>
또한 Paolo Rovelli의 설명을 읽고 KitKat 이후 Jeff Sharkey의 솔루션을 사용해보십시오 .
KitKat에는 이제 이러한 보조 공유 저장 장치와 상호 작용하기위한 공용 API가 있습니다.
새로운
Context.getExternalFilesDirs()
및Context.getExternalCacheDirs()
메서드는 기본 및 보조 장치를 포함하여 여러 경로를 반환 할 수 있습니다.그런 다음 파일을 반복하고 파일을 저장할 최적의 위치를 확인
Environment.getStorageState()
하고File.getFreeSpace()
결정할 수 있습니다.이러한 메서드는
ContextCompat
support-v4 라이브러리 에서도 사용할 수 있습니다.Android 4.4부터는 이제 외부 저장 장치에있는 파일의 소유자, 그룹 및 모드가 디렉터리 구조에 따라 합성됩니다. 이를 통해 앱은 광범위한
WRITE_EXTERNAL_STORAGE
권한 을 보유하지 않고도 외부 저장소에서 패키지 별 디렉터리를 관리 할 수 있습니다. 예를 들어, 패키지 이름이있는 앱com.example.foo
은 이제Android/data/com.example.foo/
권한없이 외부 저장 장치에 자유롭게 액세스 할 수 있습니다 . 이러한 합성 된 권한은 FUSE 데몬에서 원시 저장 장치를 래핑하여 수행됩니다.
KitKat을 사용하면 루팅없이 "완전한 솔루션"을 얻을 수있는 기회가 거의 없습니다.
Android 프로젝트는 여기에서 확실히 망쳐졌습니다. 외부 SD 카드에 대한 전체 액세스 권한이있는 앱은 없습니다.
- 파일 관리자 : 외부 SD 카드를 관리하는 데 사용할 수 없습니다. 대부분의 영역에서 그들은 읽기만 할 수 있지만 쓸 수는 없습니다.
- 미디어 앱 : 해당 앱은 쓸 수 없으므로 더 이상 미디어 컬렉션에 태그를 다시 지정하거나 다시 구성 할 수 없습니다.
- 오피스 앱 : 거의 동일
유일한 장소 3 번째의 파티 애플리케이션은 "자신의 디렉토리는"(즉, 외부 카드에 쓰기로되어 사용할 수 있습니다
/sdcard/Android/data/<package_name_of_the_app>
).제조업체 (예 : P6 용 Kitkat 업데이트로 Huawei와 같이 일부 수정)가 필요한 문제를 실제로 해결하는 유일한 방법-또는 루트 ... (Izzy의 설명은 여기에서 계속됨)
5. Android 5.0은 변경 사항과 DocumentFile 도우미 클래스를 도입했습니다 .
getStorageState
API 19에 추가되었으며 API 21에서 더 이상 사용되지 않음, 사용getExternalStorageState(File)
다음 은 KitKat의 스토리지 액세스 프레임 워크와 상호 작용하기위한 훌륭한 튜토리얼 입니다.
Lollipop에서 새로운 API와 상호 작용하는 것은 매우 유사합니다 (Jeff Sharkey의 설명) .
6. Android 6.0 Marshmallow에는 새로운 런타임 권한 모델이 도입되었습니다 .
API 레벨 23 이상인 경우 런타임에 권한 을 요청 하고 런타임에 권한 요청을 읽습니다.
Android 6.0 (API 레벨 23)부터 사용자는 앱을 설치하거나 앱을 업데이트 할 때가 아니라 앱이 실행되는 동안 앱에 권한을 부여합니다. 사용자는 권한을 취소 할 수 있습니다.
// Assume thisActivity is the current activity int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
Android 6.0 에는 앱이 런타임에 필요할 때 기능을 요청 하는 새로운 런타임 권한 모델이 도입되었습니다 . 새 모델에는
READ/WRITE_EXTERNAL_STORAGE
권한이 포함되어 있기 때문에 플랫폼은 이미 실행중인 앱을 종료하거나 다시 시작하지 않고 스토리지 액세스 권한을 동적으로 부여해야합니다. 마운트 된 모든 저장 장치의 세 가지 고유 한보기를 유지하여이를 수행합니다.
- / mnt / runtime / default는 특별한 저장소 권한이없는 앱에 표시됩니다 ...
- / mnt / runtime / read는 READ_EXTERNAL_STORAGE가있는 앱에 표시됩니다.
- / mnt / runtime / write는 WRITE_EXTERNAL_STORAGE가있는 앱에 표시됩니다.
7. Android 7.0은 외부 저장소 디렉토리에 액세스 할 수있는 단순화 된 API를 제공합니다.
범위가 지정된 디렉터리 액세스 Android 7.0에서 앱은 새 API를 사용 하여 SD 카드와 같은 이동식 미디어의 디렉터리를 포함 하여 특정 외부 저장소 디렉터리에 대한 액세스를 요청할 수 있습니다.
자세한 내용은 Scoped Directory Access 교육을 참조하십시오 .
Mark Murphy 게시물 : Be Careful with Scoped Directory Access . Android Q에서 더 이상 사용되지 않습니다 .
주 7.0에 추가 된 범위의 디렉토리 액세스가 안드로이드 Q.에서 더 이상 사용되지 않습니다 것을
특히 StorageVolume 의
createAccessIntent()
메서드 는 더 이상 사용되지 않습니다.그들은
createOpenDocumentTreeIntent()
대안으로 사용할 수 있는를 추가했습니다 .
8. Android 8.0 Oreo .. Android Q 베타 변경.
Android O부터 Storage Access Framework를 사용하면 사용자 정의 문서 공급자 가 원격 데이터 소스에있는 파일에 대해 검색 가능한 파일 설명자를 만들 수 있습니다.
Permissions , Android O 이전에 앱이 런타임에 권한을 요청하고 권한이 부여 된 경우 시스템은 동일한 권한 그룹에 속하고 매니페스트에 등록 된 나머지 권한도 앱에 잘못 부여했습니다.
Android O를 대상으로하는 앱의 경우이 동작이 수정되었습니다. 앱에는 명시 적으로 요청한 권한 만 부여됩니다. 그러나 사용자가 앱에 권한을 부여하면 해당 권한 그룹의 권한에 대한 모든 후속 요청이 자동으로 부여됩니다.
예를 들어,
READ_EXTERNAL_STORAGE
그리고WRITE_EXTERNAL_STORAGE
...
업데이트 : Android Q 이전 베타 릴리스에서는 READ_EXTERNAL_STORAGE
및 WRITE_EXTERNAL_STORAGE
권한을보다 세분화 된 미디어 별 권한 으로 일시적으로 대체했습니다 .
참고 : Google 은 베타 1에 역할 을 도입 했으며 베타 2 이전에 문서에서 제거했습니다.
참고 : 이전 베타 releases-에 도입 된 미디어 컬렉션에 대한 권한 특정은 READ_MEDIA_IMAGES
, READ_MEDIA_AUDIO
그리고 READ_MEDIA_VIDEO
- 이제 사용되지 않습니다 . 더 많은 정보:
Mark Murphy의 Q Beta 4 (최종 API) 검토 : The Death of External Storage : The End of the Saga (?)
"죽음은 삶보다 더 보편적입니다. 모든 사람이 죽지 만 모든 사람이 사는 것은 아닙니다." ― 앤드류 삭스
9. 관련 질문 및 권장 답변.
Android 4.0 이상용 외부 SD 카드 경로는 어떻게 얻을 수 있습니까?
mkdir ()은 내부 플래시 저장소 내부에서 작동하지만 SD 카드는 작동하지 않습니까?
getExternalFilesDir과 getExternalStorageDirectory ()의 차이점
일부 장치에서 getExternalFilesDirs ()가 작동하지 않는 이유는 무엇입니까?
Android 5.0 (Lollipop) 용으로 제공되는 새로운 SD 카드 액세스 API를 사용하는 방법
SAF (Storage Access Framework)를 사용한 Android SD 카드 쓰기 권한
10. 관련 버그 및 문제.
버그 : Android 6에서 getExternalFilesDirs를 사용할 때 결과에 새 파일을 만들 수 없습니다.
Lollipop의 getExternalCacheDir ()에서 반환 한 디렉토리에 쓰기 권한이 없으면 쓰기가 실패합니다.
나는 이것을 달성하는 두 가지 방법이 있다고 생각합니다.
방법 1 : ( 권한 변경으로 인해 6.0 이상에서는 작동 하지 않음 )
나는 문제없이 많은 장치 버전에서 수년간이 방법을 사용해 왔습니다. 크레딧은 내가 쓴 사람이 아니기 때문에 원본 출처 때문입니다.
문자열 디렉토리 위치 목록에서 마운트 된 모든 미디어 (실제 SD 카드 포함)를 반환 합니다 . 목록을 사용하여 저장 위치 등을 사용자에게 요청할 수 있습니다.
다음과 같이 호출 할 수 있습니다.
HashSet<String> extDirs = getStorageDirectories();
방법:
/**
* Returns all the possible SDCard directories
*/
public static HashSet<String> getStorageDirectories() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
// parse output
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase().contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase().contains("vold"))
out.add(part);
}
}
}
}
return out;
}
방법 2 :
v4 지원 라이브러리 사용
import android.support.v4.content.ContextCompat;
File
저장 위치 목록을 얻으려면 다음으로 전화하십시오 .
File[] list = ContextCompat.getExternalFilesDirs(myContext, null);
그러나 위치는 사용법이 다릅니다.
응용 프로그램이 소유 한 영구 파일을 저장할 수있는 모든 외부 저장 장치의 응용 프로그램 별 디렉터리에 대한 절대 경로를 반환합니다. 이러한 파일은 애플리케이션 내부에 있으며 일반적으로 사용자에게 미디어로 표시되지 않습니다.
여기에 반환 된 외부 저장 장치는 에뮬레이트 된 외부 저장소와 배터리 구획의 SD 카드와 같은 물리적 미디어 슬롯을 포함하여 장치의 영구적 인 부분으로 간주됩니다. 반환 된 경로에는 USB 플래시 드라이브와 같은 임시 장치가 포함되지 않습니다.
애플리케이션은 반환 된 장치의 일부 또는 전부에 데이터를 저장할 수 있습니다. 예를 들어, 앱은 사용 가능한 공간이 가장 많은 기기에 대용량 파일을 저장하도록 선택할 수 있습니다.
앱 특정 파일과 같습니다. 다른 앱에서 숨겨집니다.
또 다른 대답입니다. 여기에 게시 된 Doomknight의 답변이 Android 4.4 이하에서 수행하는 가장 좋은 방법이라고 믿기 때문에이 답변은 5.0 이상 만 표시됩니다.
이것은 원래 여기에 게시되었습니다 ( Android에서 SD 카드 크기를 얻는 방법이 있습니까? ) Android 5.0 이상에서 외부 SD 카드의 크기를 얻으려면 내가
외부 SD 카드를 다음과 같이 얻으려면 File
:
public File getExternalSdCard() {
File externalStorage = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
File storage = new File("/storage");
if(storage.exists()) {
File[] files = storage.listFiles();
for (File file : files) {
if (file.exists()) {
try {
if (Environment.isExternalStorageRemovable(file)) {
externalStorage = file;
break;
}
} catch (Exception e) {
Log.e("TAG", e.toString());
}
}
}
}
} else {
// do one of many old methods
// I believe Doomsknight's method is the best option here
}
return externalStorage;
}
참고 : "첫 번째"외부 sd 카드 만 가져 오지만 ArrayList<File>
대신 수정하고 반환 하여 첫 번째 카드를 찾은 후 File
호출하는 대신 루프를 계속할 break
수 있습니다.
다른 모든 좋은 답변 외에도이 질문에 조금 더 추가하여 독자에게 더 넓은 범위를 제공 할 수 있습니다. 여기 내 대답에서는 2 개의 셀 수있는 리소스를 사용하여 외부 저장소를 제공합니다.
첫 번째 리소스는 Android 프로그래밍, The Big Nerd Ranch Guide 2nd edition , 16 장, 294 페이지입니다.
이 책은 기본 및 외부 파일 및 디렉토리 방법을 설명합니다. 귀하의 질문과 관련이있을 수있는 이력서를 작성하려고 노력할 것입니다.
책의 다음 부분 :
외부 저장
사진은 화면의 한 공간 이상을 필요로합니다. 전체 크기 사진은 너무 커서 SQLite 데이터베이스에 보관할 수 없으며 Intent
. 그들은 장치의 파일 시스템에 살 장소가 필요합니다. 일반적으로 개인 저장소에 보관합니다. SQLite 데이터베이스를 저장하기 위해 개인 저장소를 사용했음을 상기하십시오. 같은 방법으로 Context.getFileStreamPath(String)
하고 Context.getFilesDir()
, 당신도 일반 파일과 같은 일을 할 수 (에 SQLite 데이터베이스의 삶을 하위 폴더 데이터베이스에 인접한 하위 폴더에 살 것이다)
컨텍스트의 기본 파일 및 디렉토리 메소드
| Method |
|---------------------------------------------------------------------------------------|
|File getFilesDir() |
| - Returns a handle to the directory for private application files. |
| |
|FileInputStream openFileInput(String name) |
| - Opens an existing file for input (relative to the files directory). |
| |
|FileOutputStream openFileOutput(String name, int mode) |
| - Opens a file for output, possibly creating it (relative to the files directory). |
| |
|File getDir(String name, int mode) |
| - Gets (and possibly creates) a subdirectory within the files directory. |
| |
|String[] fileList() |
| - Gets a list of file names in the main files directory, such as for use with |
| openFileInput(String). |
| |
|File getCacheDir() |
| - Returns a handle to a directory you can use specifically for storing cache files. |
| You should take care to keep this directory tidy and use as little space as possible|
현재 응용 프로그램 만 사용해야하는 파일을 저장하는 경우 이러한 방법이 정확히 필요한 것입니다.
반면에 해당 파일에 쓰기 위해 다른 응용 프로그램이 필요한 경우 운이 좋지 않습니다.에 Context.MODE_WORLD_READABLE
전달할 수 있는 플래그 가 있지만 openFileOutput(String, int)
더 이상 사용되지 않으며 최신 장치에 미치는 영향이 완전히 신뢰할 수 없습니다. 다른 앱과 공유 할 파일을 저장하거나 다른 앱 (저장된 사진과 같은 파일)에서 파일을받는 경우, 대신 외부 저장소에 저장해야합니다.
외부 저장소에는 기본 저장소와 다른 모든 저장소가 있습니다. 모든 Android 기기에는 외부 저장소를위한 위치가 하나 이상 있습니다. 기본 위치는에서 반환 한 폴더에 Environment.getExternalStorageDirectory()
있습니다. 이것은 SD 카드 일 수 있지만 요즘에는 장치 자체에 더 일반적으로 통합됩니다. 일부 장치에는 추가 외부 저장소가있을 수 있습니다. 그것은 "기타 모든 것"에 속합니다.
컨텍스트는 외부 저장소에 액세스하기위한 몇 가지 방법도 제공합니다. 이러한 방법은 기본 외부 저장소에 액세스하는 쉬운 방법과 다른 모든 것을 얻을 수있는 다소 쉬운 방법을 제공합니다. 이러한 모든 메서드는 공개적으로 사용 가능한 위치에도 파일을 저장하므로주의해야합니다.
컨텍스트의 외부 파일 및 디렉터리 메서드
| Method |
| --------------------------------------------------------------------------------------|
|File getExternalCacheDir() |
| - Returns a handle to a cache folder in primary external storage. Treat it like you do|
| getCacheDir(), except a little more carefully. Android is even less likely to clean |
| up this folder than the private storage one. |
| |
|File[] getExternalCacheDirs() |
| - Returns cache folders for multiple external storage locations. |
| |
|File getExternalFilesDir(String) |
| - Returns a handle to a folder on primary external storage in which to store regular |
| files. If you pass in a type String, you can access a specific subfolder dedicated |
| to a particular type of content. Type constants are defined in Environment, where |
| they are prefixed with DIRECTORY_. |
| For example, pictures go in Environment.DIRECTORY_PICTURES. |
| |
|File[] getExternalFilesDirs(String) |
| - Same as getExternalFilesDir(String), but returns all possible file folders for the |
| given type. |
| |
|File[] getExternalMediaDirs() |
| - Returns handles to all the external folders Android makes available for storing |
| media – pictures, movies, and music. What makes this different from calling |
| getExternalFilesDir(Environment.DIRECTORY_PICTURES) is that the media scanner |
| automatically scans this folder. The media scanner makes files available to |
| applications that play music, or browse movies and photos, so anything that you |
| put in a folder returned by getExternalMediaDirs() will automatically appear in |
| those apps. |
기술적으로 일부 장치는 외부 저장 장치로 이동식 SD 카드를 사용하기 때문에 위에 제공된 외부 폴더를 사용하지 못할 수 있습니다. 실제로 이것은 거의 문제가되지 않는데, 거의 모든 최신 장치에는 "외부"저장소를위한 분리 불가능한 내부 저장소가 있기 때문입니다. 따라서 그것을 설명하기 위해 극단적 인 길이로 갈 가치가 없습니다. 그러나 가능성을 방지하기 위해 간단한 코드를 포함하는 것이 좋습니다.
외부 저장소 권한
일반적으로 외부 저장소에서 쓰기 또는 읽기 권한이 필요합니다. 권한은 <uses-permission>
태그를 사용하여 매니페스트에 입력하는 잘 알려진 문자열 값 입니다. 그들은 Android가 권한을 요청하기를 바라는 작업을 수행하고 싶다고 Android에 알립니다.
여기에서 Android는 책임감을 강화하기 위해 권한을 요청하기를 기대합니다. Android에 외부 저장소에 액세스해야한다고 알려 주면 Android는 사용자가 애플리케이션을 설치하려고 할 때 이것이 수행하는 작업 중 하나임을 사용자에게 알립니다. 이렇게하면 SD 카드에 항목을 저장하기 시작할 때 아무도 놀라지 않습니다.
Android 4.4, KitKat에서는이 제한을 완화했습니다. Context.getExternalFilesDir(String)
앱에 특정한 폴더를 반환 하므로 거기에있는 파일을 읽고 쓸 수 있기를 원할 것입니다. 따라서 Android 4.4 (API 19) 이상에서는이 폴더에 대해이 권한이 필요하지 않습니다. (그러나 다른 종류의 외부 저장소에는 여전히 필요합니다.)
외부 저장소 읽기 권한을 요청하는 행을 매니페스트에 추가하지만 API 목록 16.5까지만 외부 저장소 권한 요청 ( AndroidManifest.xml
)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.criminalintent" >
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
maxSdkVersion 속성을 사용하면 앱이 API 19, Android KitKat보다 오래된 Android 버전에서만이 권한을 요청하도록합니다. 외부 저장소 읽기만 요청하는 것입니다. WRITE_EXTERNAL_STORAGE
권한 도 있지만 필요하지 않습니다. 외부 저장소에 아무것도 쓰지 않습니다. 카메라 앱이 자동으로 수행합니다.
두 번째 리소스는이 링크로 모든 것을 읽을 수 있지만 외부 저장소 사용 섹션으로 이동할 수도 있습니다 .
참고:
- Android 저장 옵션 : https://developer.android.com/guide/topics/data/data-storage.html
- 도서 사이트 : https://www.bignerdranch.com/we-write/android-programming
더 많은 읽기 자료 :
- https://developer.android.com/reference/android/os/Environment.html#getExternalStoragePublicDirectory%28java.lang.String%29
- https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_PICTURES
- https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_MOVIES
- Android 4.0 이상용 외부 SD 카드 경로는 어떻게 얻을 수 있습니까?
면책 조항 : 이 정보는 작성자의 허가를 받아 Android 프로그래밍 : The Big Nerd Ranch Guide에서 가져온 것입니다. 이 책에 대한 자세한 내용을 보거나 사본을 구입하려면 bignerdranch.com을 방문하십시오.
이 주제는 약간 오래되었지만 해결책을 찾고 있었고 몇 가지 조사 끝에 아래 코드를 사용하여 제 지식에 따라 다양한 장치에서 작동하는 사용 가능한 "외부"마운트 지점 목록을 검색했습니다.
기본적으로 사용 가능한 마운트 지점을 읽고, 유효하지 않은 지점을 필터링하고, 나머지 지점에 액세스 할 수 있는지 테스트하고 모든 조건이 충족되면 추가합니다.
물론 코드를 호출하기 전에 필요한 권한을 부여해야합니다.
// Notice: FileSystemDevice is just my own wrapper class. Feel free to replace it with your own.
private List<FileSystemDevice> getDevices() {
List<FileSystemDevice> devices = new ArrayList<>();
// Add default external storage if available.
File sdCardFromSystem = null;
switch(Environment.getExternalStorageState()) {
case Environment.MEDIA_MOUNTED:
case Environment.MEDIA_MOUNTED_READ_ONLY:
case Environment.MEDIA_SHARED:
sdCardFromSystem = Environment.getExternalStorageDirectory();
break;
}
if (sdCardFromSystem != null) {
devices.add(new FileSystemDevice(sdCardFromSystem));
}
// Read /proc/mounts and add all mount points that are available
// and are not "special". Also, check if the default external storage
// is not contained inside the mount point.
try {
FileInputStream fs = new FileInputStream("/proc/mounts");
String mounts = IOUtils.toString(fs, "UTF-8");
for(String line : mounts.split("\n")) {
String[] parts = line.split(" ");
// parts[0] - mount type
// parts[1] - mount point
if (parts.length > 1) {
try {
// Skip "special" mount points and mount points that can be accessed
// directly by Android's functions.
if (parts[0].equals("proc")) { continue; }
if (parts[0].equals("rootfs")) { continue; }
if (parts[0].equals("devpts")) { continue; }
if (parts[0].equals("none")) { continue; }
if (parts[0].equals("sysfs")) { continue; }
if (parts[0].equals("selinuxfs")) { continue; }
if (parts[0].equals("debugfs")) { continue; }
if (parts[0].equals("tmpfs")) { continue; }
if (parts[1].equals(Environment.getRootDirectory().getAbsolutePath())) { continue; }
if (parts[1].equals(Environment.getDataDirectory().getAbsolutePath())) { continue; }
if (parts[1].equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { continue; }
// Verify that the mount point is accessible by listing its content.
File file = new File(parts[1]);
if (file.listFiles() != null) {
try {
// Get canonical path for case it's just symlink to another mount point.
String devPath = file.getCanonicalPath();
for(FileSystemDevice device : devices) {
if (!devices.contains(devPath)) {
devices.add(new FileSystemDevice(new File(devPath)));
}
}
} catch (Exception e) {
// Silently skip the exception as it can only occur if the mount point is not valid.
e.printStackTrace();
}
}
} catch (Exception e) {
// Silently skip the exception as it can only occur if the mount point is not valid.
e.printStackTrace();
}
}
}
fs.close();
} catch (FileNotFoundException e) {
// Silently skip the exception as it can only occur if the /proc/mounts file is unavailable.
// Possibly, another detection method can be called here.
e.printStackTrace();
} catch (IOException e) {
// Silently skip the exception as it can only occur if the /proc/mounts file is unavailable.
// Possibly, another detection method can be called here.
e.printStackTrace();
}
return devices;
}
다음은 외부 저장소에 새 파일을 만드는 방법입니다 (장치에있는 경우 SDCard, 그렇지 않은 경우 장치에있는 외부 저장소). "foldername"을 원하는 대상 폴더 이름으로 바꾸고 "filename"을 저장하려는 파일 이름으로 바꾸십시오. 물론 여기에서 일반 파일을 저장하는 방법을 볼 수 있습니다. 이제 이미지를 저장하는 방법을 검색 할 수 있습니다. 여기 또는 파일에있는 모든 것이 있습니다.
try {
File dir = new File(Environment.getExternalStorageDirectory() + "/foldername/");
if (!dir.exists()){
dir.mkdirs();
}
File sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileName );
int num = 1;
String fileNameAux = fileName;
while (sdCardFile.exists()){
fileNameAux = fileName+"_"+num;
sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileNameAux);
num++;
}
또한 파일이 존재하는지 제어하고 새 파일 이름 끝에 번호를 추가하여 저장합니다.
도움이 되었기를 바랍니다.
편집 : 죄송합니다, <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
매니페스트에서 요청해야하는 것을 잊었습니다 (또는 Marshmallow에서 선호하는 경우 프로그래밍 방식으로)
Marshmallow 이하 버전의 경우 매니페스트에서 직접 권한을 부여 할 수 있습니다.
그러나 Marshmallow 이상을 사용하는 장치의 경우 런타임에 권한을 부여해야합니다.
사용하여
Environment.getExternalStorageDirectory();
외부 SD 카드 (장착 된 카드)에 직접 액세스 할 수 있습니다.
참고 URL : https://stackoverflow.com/questions/40068984/universal-way-to-write-to-external-sd-card-on-android
'Programing' 카테고리의 다른 글
조각 내부 클래스는 정적이어야합니다. (0) | 2020.11.05 |
---|---|
서비스 / 팩토리에서 컨트롤러로 변수 바인딩 (0) | 2020.11.05 |
인터페이스를 사용하는 이유는 무엇입니까? (0) | 2020.11.05 |
C #에서 (디렉터리) 경로를 어떻게 비교할 수 있습니까? (0) | 2020.11.04 |
CSS 페이지 나누기를 적용하여 행이 많은 표를 인쇄하는 방법은 무엇입니까? (0) | 2020.11.04 |