Android 4.0 이상용 외부 SD 카드 경로는 어떻게 얻을 수 있습니까?


93

Samsung Galaxy S3 에는 외부 SD 카드 슬롯이 /mnt/extSdCard있습니다.

이 경로를 어떻게 얻을 수 Environment.getExternalStorageDirectory()있습니까?

이 반환 mnt/sdcard되고 외부 SD 카드에 대한 API를 찾을 수 없습니다. (또는 일부 태블릿의 이동식 USB 저장소.)


왜 이것을 원합니까? Android에서 이와 같은 작업을 수행하는 것은 매우 바람직하지 않습니다. 아마도 당신이 동기를 공유한다면 우리는 이런 유형의 일에 대한 모범 사례의 방향을 알려줄 수 있습니다.
rharter 2014

2
사용자가 전화기를 변경하고 SD 카드를 새 전화기에 연결하고 첫 번째 전화기에서는 / sdcard이고 두 번째 전화기에서는 / mnt / extSdCard이며 파일 경로를 사용하는 모든 것이 충돌합니다. 상대 경로에서 실제 경로를 생성해야합니다.
Romulus Urakagi Ts'ai

이제이 주제는 오래되었지만 도움이 될 수 있습니다. 이 방법을 사용해야합니다. System.getenv (); 장치에 연결된 모든 스토리지에 액세스하려면 프로젝트 환경 3을 참조하십시오. github.com/omidfaraji/Environment3
Omid


Nougat까지 작동하는 솔루션은 다음과 같습니다. stackoverflow.com/a/40205116/5002496
Gokul NC

답변:


58

여기 에서 찾은 솔루션에 대한 변형이 있습니다.

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;
}

원래 방법을 테스트하고

  • Huawei X3 (재고)
  • Galaxy S2 (재고)
  • Galaxy S3 (재고)

테스트를 받았을 때 어떤 안드로이드 버전이 있었는지 잘 모르겠습니다.

수정 된 버전을 다음으로 테스트했습니다.

  • Moto Xoom 4.1.2 (재고)
  • OTG 케이블을 사용하는 Galaxy Nexus (cyanogenmod 10)
  • HTC Incredible (cyanogenmod 7.2)은 내부 및 외부 모두를 반환했습니다. 이 장치는 getExternalStorage ()가 대신 sdcard에 대한 경로를 반환하므로 내부가 거의 사용되지 않는다는 점에서 다소 이상한 것입니다.

주 저장소로 sdcard를 사용하는 일부 단일 저장 장치

  • HTC G1 (시아 노겐 모드 6.1)
  • HTC G1 (재고)
  • HTC Vision / G2 (재고)

Incredible을 제외하고 이러한 모든 장치는 이동식 저장소 만 반환했습니다. 내가해야 할 몇 가지 추가 검사가있을 수 있지만 지금까지 찾은 솔루션보다 적어도 조금 더 낫습니다.


3
나는 원래가을 / mnt / SD 카드와 같은 대문자가 포함되어있는 경우이 솔루션 유효하지 않은 폴더 이름을 줄 것이라고 생각
유진 포포비치

1
일부 Motorola, Samsung 및 LG 장치에서 테스트했으며 완벽하게 작동했습니다.
pepedeutico

2
@KhawarRaza,이 방법은 kitkat에서 작동을 중지했습니다. Dmitriy Lozenko의 방법을 사용하여 pre-ics 장치를 지원하는 경우이를 대체하십시오.
Gnathonic

1
HUAWEI Honor 3c에서 작동합니다. 감사.
li2

2
이것은 Galaxy Note 3에서 나를 위해 4.0+에서 / storage 대신 / mnt를 반환합니다
ono

54

시스템의 모든 SD 카드에 대한 경로를 얻는 더 안정적인 방법을 찾았습니다. 이는 모든 Android 버전에서 작동하며 모든 저장소 (에뮬레이션 포함)에 대한 경로를 반환합니다.

내 모든 장치에서 올바르게 작동합니다.

PS : 환경 클래스의 소스 코드를 기반으로합니다.

private static final Pattern DIR_SEPORATOR = Pattern.compile("/");

/**
 * Raturns all available SD-Cards in the system (include emulated)
 *
 * Warning: Hack! Based on Android source code of version 4.3 (API 18)
 * Because there is no standart way to get it.
 * TODO: Test on future Android versions 4.4+
 *
 * @return paths to all available SD-Cards in the system (include emulated)
 */
public static String[] getStorageDirectories()
{
    // Final set of paths
    final Set<String> rv = new HashSet<String>();
    // Primary physical SD-CARD (not emulated)
    final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
    // All Secondary SD-CARDs (all exclude primary) separated by ":"
    final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    // Primary emulated SD-CARD
    final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
    if(TextUtils.isEmpty(rawEmulatedStorageTarget))
    {
        // Device has physical external storage; use plain paths.
        if(TextUtils.isEmpty(rawExternalStorage))
        {
            // EXTERNAL_STORAGE undefined; falling back to default.
            rv.add("/storage/sdcard0");
        }
        else
        {
            rv.add(rawExternalStorage);
        }
    }
    else
    {
        // Device has emulated storage; external storage paths should have
        // userId burned into them.
        final String rawUserId;
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
        {
            rawUserId = "";
        }
        else
        {
            final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
            final String[] folders = DIR_SEPORATOR.split(path);
            final String lastFolder = folders[folders.length - 1];
            boolean isDigit = false;
            try
            {
                Integer.valueOf(lastFolder);
                isDigit = true;
            }
            catch(NumberFormatException ignored)
            {
            }
            rawUserId = isDigit ? lastFolder : "";
        }
        // /storage/emulated/0[1,2,...]
        if(TextUtils.isEmpty(rawUserId))
        {
            rv.add(rawEmulatedStorageTarget);
        }
        else
        {
            rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
        }
    }
    // Add all secondary storages
    if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
    {
        // All Secondary SD-CARDs splited into array
        final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
        Collections.addAll(rv, rawSecondaryStorages);
    }
    return rv.toArray(new String[rv.size()]);
}

5
DIR_SEPORATOR이 (가) 무엇이라고 생각 했습니까?
cYrixmorten

1
해당 코드의 상단에 제공 @cYrixmorten ... 자사 Pattern에 대한/
ANKIT 반살

누구든지 Lolipop에서이 솔루션을 테스트 했습니까?
lenrok258

lenovo P780에서이 방법을 시도했지만 작동하지 않습니다.
Satheesh 쿠마

2
Lollipop까지 작동합니다. (/ 6.0.1 승 갤럭시 탭 A) 산들 바람에 작동하지 System.getenv("SECONDARY_STORAGE")반환 /storage/sdcard1. 해당 위치는 존재하지 않지만 실제 위치는입니다 /storage/42CE-FD0A. 여기서 42CE-FD0A는 포맷 된 파티션의 볼륨 일련 번호입니다.
DaveAlden

32

나는 이것을 사용해야하는 외부 sdcard를 사용한다고 생각합니다.

new File("/mnt/external_sd/")

또는

new File("/mnt/extSdCard/")

당신의 경우에는 ...

대신 Environment.getExternalStorageDirectory()

나를 위해 작동합니다. 먼저 mnt 디렉토리에있는 내용을 확인하고 거기에서 작업해야합니다.


사용할 sdcard를 선택하려면 몇 가지 유형의 선택 방법을 사용해야합니다.

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}

1
사실 하드 코드 경로보다는 방법을 원하지만 / mnt / 목록은 괜찮을 것입니다. 감사합니다.
Romulus Urakagi Ts'ai

문제 없어요. 경로를 선택하는 설정에 옵션이있는 다음 방법을 사용하여 검색하십시오.
FabianCook

10
불행히도 외부 SD 카드는 / mnt와 다른 폴더에있을 수 있습니다. 예 : Samsung GT-I9305에서는 / storage / extSdCard이고 내장형 SD 카드에는 / storage / sdcard0 경로가 있습니다
iseeall

2
그러면 sdcard 위치를 얻은 다음 부모 디렉토리를 얻습니다.
FabianCook

Android 4.0+ 용 외부 SD 카드 (OTG 케이블을 통해 태블릿에 연결된 pendrive 용)의 경로는 어떻게됩니까?
osimer pothe 2015.02.09

15

모든 외부 저장소 ( SD 카드 또는 내부 비 이동식 저장소 )를 검색하려면 다음 코드를 사용할 수 있습니다.

final String state = Environment.getExternalStorageState();

if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...           
    //Retrieve the primary External Storage:
    final File primaryExternalStorage = Environment.getExternalStorageDirectory();

    //Retrieve the External Storages root directory:
    final String externalStorageRootDir;
    if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) {  // no parent...
        Log.d(TAG, "External Storage: " + primaryExternalStorage + "\n");
    }
    else {
        final File externalStorageRoot = new File( externalStorageRootDir );
        final File[] files = externalStorageRoot.listFiles();

        for ( final File file : files ) {
            if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) {  // it is a real directory (not a USB drive)...
                Log.d(TAG, "External Storage: " + file.getAbsolutePath() + "\n");
            }
        }
    }
}

또는 System.getenv ( "EXTERNAL_STORAGE") 를 사용하여 기본 외부 저장소 디렉터리 (예 : "/ storage / sdcard0" )를 검색하고 System.getenv ( "SECONDARY_STORAGE") 를 사용하여 모든 보조 디렉터리 목록 (예 : " / storage / extSdCard : / storage / UsbDriveA : / storage / UsbDriveB " ). 이 경우에도 USB 드라이브를 제외하기 위해 보조 디렉토리 목록을 필터링 할 수 있습니다.

어쨌든 하드 코딩 된 경로를 사용하는 것은 항상 잘못된 접근 방식입니다 (특히 모든 제조업체가 원하는대로 변경할 수있는 경우).


System.getenv ( "SECONDARY_STORAGE")는 Nexus 5 및 Nexus 7에서 OTG 케이블을 통해 연결된 USB 장치에서 작동하지 않았습니다.
Khawar Raza

누가까지 작동하는 솔루션은 다음과 같습니다. stackoverflow.com/a/40205116/5002496
Gokul NC

14

Asus Zenfone2 , Marshmallow 6.0.1 을 확인하고 솔루션이 작동하지 않을 때까지 Dmitriy Lozenko 의 솔루션 을 사용 하고있었습니다. EMULATED_STORAGE_TARGET , 특히 microSD 경로 (예 : / storage / F99C-10F4 /)를 가져올 때 솔루션이 실패했습니다 . 에뮬레이트 된 애플리케이션 경로에서 직접 에뮬레이트 된 루트 경로를 가져 오고 더 알려진 전화 모델 별 물리적 경로를 추가 하도록 코드를 편집했습니다 .context.getExternalFilesDirs(null);

우리의 삶을 더 쉽게 만들기 위해 여기 에 도서관을 만들었습니다 . gradle, maven, sbt 및 leiningen 빌드 시스템을 통해 사용할 수 있습니다.

구식 방식이 마음에 들면 여기 에서 직접 파일을 복사하여 붙여 넣을 수도 있지만 수동으로 확인하지 않고는 향후 업데이트가 있는지 알 수 없습니다.

질문이나 제안 사항이 있으면 알려주십시오.


1
나는 이것을 광범위하게 사용하지 않았지만 이것은 내 문제에 잘 작동합니다.
David

1
훌륭한 솔루션! 그러나 한 가지 참고 사항-응용 프로그램이 활성화되어있을 때 SD 카드를 제거하면 파일 중 하나에 대해 context.getExternalFilesDirs(null)반환 null되고 다음 루프에서 예외가 발생합니다. if (file == null) continue;루프의 첫 번째 줄로 추가하는 것이 좋습니다 .
Koger

1
@Koger이 제안을 주셔서 감사합니다, 나는 그것을 추가 내 대답에 오타를 해결 한
HendraWD

13

좋은 소식! KitKat에는 이제 이러한 보조 공유 저장 장치와 상호 작용하기위한 공용 API가 있습니다.

새로운 Context.getExternalFilesDirs()Context.getExternalCacheDirs()메서드는 기본 및 보조 장치를 포함하여 여러 경로를 반환 할 수 있습니다. 그런 다음 파일을 반복하고 파일을 저장할 최적의 위치를 확인 Environment.getStorageState()하고 File.getFreeSpace()결정할 수 있습니다. 이러한 메서드는 ContextCompatsupport-v4 라이브러리 에서도 사용할 수 있습니다.

또한에서 반환 한 디렉토리 만 사용하는 데 관심이있는 경우 Context더 이상 READ_또는 WRITE_EXTERNAL_STORAGE권한이 필요하지 않습니다 . 앞으로는 추가 권한 없이도 이러한 디렉터리에 대한 읽기 / 쓰기 액세스 권한을 항상 갖게됩니다.

앱은 다음과 같이 권한 요청을 종료하여 이전 장치에서 계속 작업 할 수도 있습니다.

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />

감사합니다. 나는 당신이 작성한 것을 따르는 몇 가지 코드를 만들었고 모든 SD 카드 경로를 찾는 데 잘 작동한다고 생각합니다. 당신은 볼 수 있습니까? 여기 : stackoverflow.com/a/27197248/878126. BTW, "getFreeSpace"를 사용하면 안됩니다. 이론적으로 여유 공간은 런타임 중에 변경 될 수 있기 때문입니다.
안드로이드 개발자


10

모든 외부 SD 카드에 액세스하기 위해 다음을 수행했습니다.

와:

File primaryExtSd=Environment.getExternalStorageDirectory();

기본 외부 SD에 대한 경로를 얻습니다.

File parentDir=new File(primaryExtSd.getParent());

기본 외부 저장소의 상위 디렉토리를 가져오고 모든 외부 SD의 상위 디렉토리이기도합니다. 이제 모든 저장소를 나열하고 원하는 저장소를 선택할 수 있습니다.

유용하기를 바랍니다.


이것은 받아 들여지는 대답이어야하며 유용합니다.
Meanman

9

SD 카드 경로 목록을 얻는 방법은 다음과 같습니다 (기본 외부 저장소 제외).

  /**
   * returns a list of all available sd cards paths, or null if not found.
   * 
   * @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public static List<String> getSdCardPaths(final Context context,final boolean includePrimaryExternalStorage)
    {
    final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
    if(externalCacheDirs==null||externalCacheDirs.length==0)
      return null;
    if(externalCacheDirs.length==1)
      {
      if(externalCacheDirs[0]==null)
        return null;
      final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
      if(!Environment.MEDIA_MOUNTED.equals(storageState))
        return null;
      if(!includePrimaryExternalStorage&&VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB&&Environment.isExternalStorageEmulated())
        return null;
      }
    final List<String> result=new ArrayList<>();
    if(includePrimaryExternalStorage||externalCacheDirs.length==1)
      result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
    for(int i=1;i<externalCacheDirs.length;++i)
      {
      final File file=externalCacheDirs[i];
      if(file==null)
        continue;
      final String storageState=EnvironmentCompat.getStorageState(file);
      if(Environment.MEDIA_MOUNTED.equals(storageState))
        result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
      }
    if(result.isEmpty())
      return null;
    return result;
    }

  /** Given any file/folder inside an sd card, this will return the path of the sd card */
  private static String getRootOfInnerSdCardFolder(File file)
    {
    if(file==null)
      return null;
    final long totalSpace=file.getTotalSpace();
    while(true)
      {
      final File parentFile=file.getParentFile();
      if(parentFile==null||parentFile.getTotalSpace()!=totalSpace||!parentFile.canRead())
        return file.getAbsolutePath();
      file=parentFile;
      }
    }

externalCacheDirs[0].getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath()이것은 재난의 비결입니다. 이 작업을 수행하는 경우 원하는 경로를 하드 코딩하는 것이 좋습니다. 부모가 4 개가 아닌 다른 캐시 디렉터리를 가져 오면 NullPointerException 또는 잘못된 경로가 발생하기 때문입니다.
rharter 2014

@rharter 맞습니다. 이 문제도 해결되었습니다. 한번 봐주세요.
안드로이드 개발자

문서 ( developer.android.com/reference/android/content/… )에는 "여기에 반환 된 외부 저장 장치는 에뮬레이션 된 외부 저장소와 배터리의 SD 카드와 같은 물리적 미디어 슬롯을 포함하여 장치의 영구적 인 부분으로 간주됩니다. 반환 된 경로에는 USB 플래시 드라이브와 같은 임시 장치가 포함되지 않습니다. " SD 카드 슬롯도 포함되어 있지 않은 것 같습니다. 즉, 배터리 아래로 이동하지 않고 SD 카드를 제거 할 수있는 곳입니다. 하지만 말하기 어렵습니다.
LarsH

1
@LarsH 음, SGS3 (및 실제 SD 카드)에서 직접 테스트 했으므로 다른 장치에서도 작동해야한다고 생각합니다.
안드로이드 개발자

5

여러분, 특히 @SmartLemon이 제공 한 단서에 감사드립니다. 해결책을 찾았습니다. 다른 사람이 필요한 경우 여기에 최종 솔루션을 넣습니다 (첫 번째로 나열된 외부 SD 카드 찾기).

public File getExternalSDCardDirectory()
{
    File innerDir = Environment.getExternalStorageDirectory();
    File rootDir = innerDir.getParentFile();
    File firstExtSdCard = innerDir ;
    File[] files = rootDir.listFiles();
    for (File file : files) {
        if (file.compareTo(innerDir) != 0) {
            firstExtSdCard = file;
            break;
        }
    }
    //Log.i("2", firstExtSdCard.getAbsolutePath().toString());
    return firstExtSdCard;
}

외부 SD 카드가 없으면 온보드 저장소로 돌아갑니다. sdcard가 존재하지 않으면 사용하겠습니다. 변경해야 할 수도 있습니다.


1
Android 버전 19에서는 null이 발생합니다. rootDir.listFiles ()는 null을 반환합니다. 넥서스 7, 에뮬레이터 및 갤럭시 노트로 테스트했습니다.
Manu Zi

3

내 코드를 참조하고 도움이되기를 바랍니다.

    Runtime runtime = Runtime.getRuntime();
    Process proc = runtime.exec("mount");
    InputStream is = proc.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    String line;
    String mount = new String();
    BufferedReader br = new BufferedReader(isr);
    while ((line = br.readLine()) != null) {
        if (line.contains("secure")) continue;
        if (line.contains("asec")) continue;

        if (line.contains("fat")) {//TF card
            String columns[] = line.split(" ");
            if (columns != null && columns.length > 1) {
                mount = mount.concat("*" + columns[1] + "\n");
            }
        } else if (line.contains("fuse")) {//internal storage
            String columns[] = line.split(" ");
            if (columns != null && columns.length > 1) {
                mount = mount.concat(columns[1] + "\n");
            }
        }
    }
    txtView.setText(mount);

2

실제로 일부 장치에서는 외부 sdcard 기본 이름이로 표시되고 extSdCard다른 경우에는 sdcard1.

이 코드 조각은 정확한 경로를 찾는 데 도움이되며 외부 장치의 경로를 검색하는 데 도움이됩니다.

String sdpath,sd1path,usbdiskpath,sd0path;    
        if(new File("/storage/extSdCard/").exists())
            {
               sdpath="/storage/extSdCard/";
               Log.i("Sd Cardext Path",sdpath);
            }
        if(new File("/storage/sdcard1/").exists())
         {
              sd1path="/storage/sdcard1/";
              Log.i("Sd Card1 Path",sd1path);
         }
        if(new File("/storage/usbcard1/").exists())
         {
              usbdiskpath="/storage/usbcard1/";
              Log.i("USB Path",usbdiskpath);
         }
        if(new File("/storage/sdcard0/").exists())
         {
              sd0path="/storage/sdcard0/";
              Log.i("Sd Card0 Path",sd0path);
         }

이것은 나를 크게 도왔습니다.
Droid Chris

'/ storage / external_sd'(LG G3 KitKat)를 사용하는 일부 기기가 있고, Marshmallow에는 몇 가지 다른 체계가 있으며, Android 6.0을 사용하는 Nexus 5X의 내부 저장소는 '/ mnt / sdcard'이고 외부 SD 카드는 '/ storage 아래에 있습니다. / XXXX-XXXX "
AB

2

예. 다른 제조업체는 Samsung Tab 3의 extsd와 같이 다른 SD 카드 이름을 사용하고 다른 삼성 장치는이 다른 제조업체가 다른 이름을 사용하는 것처럼 sdcard를 사용합니다.

나는 당신과 같은 요구 사항이있었습니다. 그래서 나는 내 프로젝트에서 당신을 위해 샘플 예제를 만들었습니다.이 링크 androi-dirchooser 라이브러리를 사용하는 Android 디렉토리 선택기 예제로 이동하십시오. 이 예에서는 SD 카드를 감지하고 모든 하위 폴더를 나열하며 기기에 SD 카드가 두 개 이상 있는지도 감지합니다.

코드의 일부는 다음과 같습니다. 전체 예제를 보려면 Android Directory Chooser 링크로 이동하십시오.

/**
* Returns the path to internal storage ex:- /storage/emulated/0
 *
* @return
 */
private String getInternalDirectoryPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath();
 }

/**
 * Returns the SDcard storage path for samsung ex:- /storage/extSdCard
 *
 * @return
 */
    private String getSDcardDirectoryPath() {
    return System.getenv("SECONDARY_STORAGE");
}


 mSdcardLayout.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        String sdCardPath;
        /***
         * Null check because user may click on already selected buton before selecting the folder
         * And mSelectedDir may contain some wrong path like when user confirm dialog and swith back again
         */

        if (mSelectedDir != null && !mSelectedDir.getAbsolutePath().contains(System.getenv("SECONDARY_STORAGE"))) {
            mCurrentInternalPath = mSelectedDir.getAbsolutePath();
        } else {
            mCurrentInternalPath = getInternalDirectoryPath();
        }
        if (mCurrentSDcardPath != null) {
            sdCardPath = mCurrentSDcardPath;
        } else {
            sdCardPath = getSDcardDirectoryPath();
        }
        //When there is only one SDcard
        if (sdCardPath != null) {
            if (!sdCardPath.contains(":")) {
                updateButtonColor(STORAGE_EXTERNAL);
                File dir = new File(sdCardPath);
                changeDirectory(dir);
            } else if (sdCardPath.contains(":")) {
                //Multiple Sdcards show root folder and remove the Internal storage from that.
                updateButtonColor(STORAGE_EXTERNAL);
                File dir = new File("/storage");
                changeDirectory(dir);
            }
        } else {
            //In some unknown scenario at least we can list the root folder
            updateButtonColor(STORAGE_EXTERNAL);
            File dir = new File("/storage");
            changeDirectory(dir);
        }


    }
});

2

이 솔루션 (이 질문에 대한 다른 답변에서 모아진) System.getenv("SECONDARY_STORAGE")은 Marshmallow와 함께 사용되지 않는 사실 (@ono에서 언급 한대로)을 처리합니다 .

테스트 및 작업 :

  • Samsung Galaxy Tab 2 (Android 4.1.1-재고)
  • 삼성 Galaxy Note 8.0 (Android 4.2.2-재고)
  • 삼성 Galaxy S4 (Android 4.4-재고)
  • 삼성 Galaxy S4 (Android 5.1.1-Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1-재고)

    /**
     * Returns all available external SD-Card roots in the system.
     *
     * @return paths to all available external SD-Card roots in the system.
     */
    public static String[] getStorageDirectories() {
        String [] storageDirectories;
        String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            List<String> results = new ArrayList<String>();
            File[] externalDirs = applicationContext.getExternalFilesDirs(null);
            for (File file : externalDirs) {
                String path = file.getPath().split("/Android")[0];
                if((Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Environment.isExternalStorageRemovable(file))
                        || rawSecondaryStoragesStr != null && rawSecondaryStoragesStr.contains(path)){
                    results.add(path);
                }
            }
            storageDirectories = results.toArray(new String[0]);
        }else{
            final Set<String> rv = new HashSet<String>();
    
            if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
                final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
                Collections.addAll(rv, rawSecondaryStorages);
            }
            storageDirectories = rv.toArray(new String[rv.size()]);
        }
        return storageDirectories;
    }

조건에 rawSecondaryStoragesStr.contains(path)따라 내부 저장소도 추가 될 수 있습니다. 우리는 그 조건을 더 잘 제거 할 수 있습니다. 내 장치는 내부 저장소 디렉토리를로 System.getenv("SECONDARY_STORAGE")반환하지만 System.getenv(EXTERNAL_STORAGE)KitKat 에서는 외부 메모리 카드 경로를 반환하기 때문에 그 문제에 직면했습니다 . 서로 다른 공급 업체가 어떤 빌어 먹을 표준없이 다른이를 구현 한 것 같다 ..
Gokul NC에게

누가까지 작동하는 솔루션은 다음과 같습니다. stackoverflow.com/a/40205116/5002496
Gokul NC

1

일부 장치 (예 : samsung galaxy sII)에서는 내부 메모리 카드가 vfat 일 수 있습니다. 이 경우 참조 마지막 코드를 사용하면 경로 내부 메모리 카드 (/ mnt / sdcad)를 얻지 만 외부 카드는 얻지 못합니다. 아래 코드 참조는이 문제를 해결합니다.

static String getExternalStorage(){
         String exts =  Environment.getExternalStorageDirectory().getPath();
         try {
            FileReader fr = new FileReader(new File("/proc/mounts"));       
            BufferedReader br = new BufferedReader(fr);
            String sdCard=null;
            String line;
            while((line = br.readLine())!=null){
                if(line.contains("secure") || line.contains("asec")) continue;
            if(line.contains("fat")){
                String[] pars = line.split("\\s");
                if(pars.length<2) continue;
                if(pars[1].equals(exts)) continue;
                sdCard =pars[1]; 
                break;
            }
        }
        fr.close();
        br.close();
        return sdCard;  

     } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

1
       File[] files = null;
    File file = new File("/storage");// /storage/emulated
if (file.exists()) {
        files = file.listFiles();
            }
            if (null != files)
                for (int j = 0; j < files.length; j++) {
                    Log.e(TAG, "" + files[j]);
                    Log.e(TAG, "//--//--// " +             files[j].exists());

                    if (files[j].toString().replaceAll("_", "")
                            .toLowerCase().contains("extsdcard")) {
                        external_path = files[j].toString();
                        break;
                    } else if (files[j].toString().replaceAll("_", "")
                            .toLowerCase()
                            .contains("sdcard".concat(Integer.toString(j)))) {
                        // external_path = files[j].toString();
                    }
                    Log.e(TAG, "--///--///--  " + external_path);
                }

0

이 코드가 문제를 확실히 해결할 것이라고 확신합니다 ... 이것은 저에게 잘 작동합니다 ... \

try {
            File mountFile = new File("/proc/mounts");
            usbFoundCount=0;
            sdcardFoundCount=0;
            if(mountFile.exists())
             {
                Scanner usbscanner = new Scanner(mountFile);
                while (usbscanner.hasNext()) {
                    String line = usbscanner.nextLine();
                    if (line.startsWith("/dev/fuse /storage/usbcard1")) {
                        usbFoundCount=1;
                        Log.i("-----USB--------","USB Connected and properly mounted---/dev/fuse /storage/usbcard1" );
                    }
            }
         }
            if(mountFile.exists()){
                Scanner sdcardscanner = new Scanner(mountFile);
                while (sdcardscanner.hasNext()) {
                    String line = sdcardscanner.nextLine();
                    if (line.startsWith("/dev/fuse /storage/sdcard1")) {
                        sdcardFoundCount=1;
                        Log.i("-----USB--------","USB Connected and properly mounted---/dev/fuse /storage/sdcard1" );
                    }
            }
         }
            if(usbFoundCount==1)
            {
                Toast.makeText(context,"USB Connected and properly mounted", 7000).show();
                Log.i("-----USB--------","USB Connected and properly mounted" );
            }
            else
            {
                Toast.makeText(context,"USB not found!!!!", 7000).show();
                Log.i("-----USB--------","USB not found!!!!" );

            }
            if(sdcardFoundCount==1)
            {
                Toast.makeText(context,"SDCard Connected and properly mounted", 7000).show();
                Log.i("-----SDCard--------","SDCard Connected and properly mounted" );
            }
            else
            {
                Toast.makeText(context,"SDCard not found!!!!", 7000).show();
                Log.i("-----SDCard--------","SDCard not found!!!!" );

            }
        }catch (Exception e) {
            e.printStackTrace();
        } 

Environment.getExternalStorageDirectory ()는 외부 sdcard 또는 usb 위치의 정확한 위치를 제공해서는 안됩니다. "/ proc / mounts"위치로 구문 분석 한 다음 / dev / fuse / storage / sdcard1 "은 sdcard가 올바른지 여부를 알려줍니다. 장착 또는 쉽게 it..Cheers..Sam 얻을 수있는 USB also.This 방법에 대한 동일하지 않습니다

0

그건 사실이 아니야. / mnt / sdcard / external_sd는 SD 카드가 장착되어 있지 않아도 존재할 수 있습니다. 마운트되지 않은 경우 / mnt / sdcard / external_sd에 쓰려고하면 애플리케이션이 충돌합니다.

다음을 사용하여 먼저 SD 카드가 마운트되었는지 확인해야합니다.

boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);

7
getExternalStorageState()외부 SD 카드가 아닌 내부 저장소를 반납하면 의미가 없다고 생각합니다.
Romulus Urakagi Ts'ai 2011

이제 외부 SD 카드를 얻는 기능을 제공합니까? 한 시간 동안 주변을 검색해도 단서가 없습니다. 이 경우 많은 버전이 업데이트 된 후 이것은 매우 이상합니다.
rml

"사실이 아닙니다"라고했을 때 무엇을 말하고 있었습니까? 알아요, 2.5 년 전 이었어요 ...
LarsH

0
 String path = Environment.getExternalStorageDirectory()
                        + File.separator + Environment.DIRECTORY_PICTURES;
                File dir = new File(path);

4
작동하지 않습니다. 내부 저장소의 경로를 반환합니다. 예 : / storage / emulated / 0

0

Context.getExternalCacheDirs () 또는 Context.getExternalFilesDirs () 또는 Context.getObbDirs ()와 같은 것을 사용할 수 있습니다. 애플리케이션이 파일을 저장할 수있는 모든 외부 저장 장치에 애플리케이션 특정 디렉토리를 제공합니다.

따라서 다음과 같이-Context.getExternalCacheDirs () [i] .getParentFile (). getParentFile (). getParentFile (). getParent ()는 외부 저장 장치의 루트 경로를 가져올 수 있습니다.

이 명령은 다른 목적을위한 것이지만 다른 답변은 저에게 효과가 없었습니다.

이 링크는 나에게 좋은 포인터를 주었다-https: //possiblemobile.com/2014/03/android-external-storage/


0

System.getenv("SECONDARY_STORAGE")Marshmallow에 대해 null을 반환합니다. 이것은 모든 외부 디렉토리를 찾는 또 다른 방법입니다. 내부 / 외부 여부를 결정하는 제거 가능한지 확인할 수 있습니다.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    File[] externalCacheDirs = context.getExternalCacheDirs();
    for (File file : externalCacheDirs) {
        if (Environment.isExternalStorageRemovable(file)) {
            // It's a removable storage
        }
    }
}

0

Samsung Galaxy Tab S2 (모델 : T819Y) 에서 Dmitriy LozenkoGnathonic이 제공 한 솔루션을 사용해 보았지만 외부 SD 카드 디렉터리 경로를 검색하는 데 도움이되지 않았습니다. 명령 실행에 외부 SD 카드 디렉토리 (예 : / Storage / A5F9-15F4)에 대한 필수 경로가 포함 되었지만 정규 표현식과 일치하지 않아 반환되지 않았습니다. 삼성이 따르는 디렉토리 이름 지정 메커니즘을 얻지 못했습니다. 왜 그들이 표준 (예 : extsdcard) 에서 벗어나고 제 경우와 같이 정말 수상한 것을 생각해 냈습니다 (예 : / Storage / A5F9-15F4) . 내가 놓친 것이 있습니까? 어쨌든, Gnathonic의 정규 표현식 변경에 따라mount 솔루션은 유효한 sdcard 디렉토리를 얻는 데 도움이되었습니다.

final HashSet<String> out = new HashSet<String>();
        String reg = "(?i).*(vold|media_rw).*(sdcard|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;

이것이 유효한 솔루션인지 그리고 다른 삼성 태블릿에 대한 결과를 제공하는지 확실하지 않지만 현재 내 문제를 해결했습니다. 다음은 Android (v6.0)에서 이동식 SD 카드 경로를 검색하는 또 다른 방법입니다. 나는 안드로이드 마시맬로로 방법을 테스트했으며 작동합니다. 여기에 사용 된 접근 방식은 매우 기본적이며 다른 버전에서도 작동하지만 테스트는 필수입니다. 이에 대한 통찰력이 도움이 될 것입니다.

public static String getSDCardDirPathForAndroidMarshmallow() {

    File rootDir = null;

    try {
        // Getting external storage directory file
        File innerDir = Environment.getExternalStorageDirectory();

        // Temporarily saving retrieved external storage directory as root
        // directory
        rootDir = innerDir;

        // Splitting path for external storage directory to get its root
        // directory

        String externalStorageDirPath = innerDir.getAbsolutePath();

        if (externalStorageDirPath != null
                && externalStorageDirPath.length() > 1
                && externalStorageDirPath.startsWith("/")) {

            externalStorageDirPath = externalStorageDirPath.substring(1,
                    externalStorageDirPath.length());
        }

        if (externalStorageDirPath != null
                && externalStorageDirPath.endsWith("/")) {

            externalStorageDirPath = externalStorageDirPath.substring(0,
                    externalStorageDirPath.length() - 1);
        }

        String[] pathElements = externalStorageDirPath.split("/");

        for (int i = 0; i < pathElements.length - 1; i++) {

            rootDir = rootDir.getParentFile();
        }

        File[] files = rootDir.listFiles();

        for (File file : files) {
            if (file.exists() && file.compareTo(innerDir) != 0) {

                // Try-catch is implemented to prevent from any IO exception
                try {

                    if (Environment.isExternalStorageRemovable(file)) {
                        return file.getAbsolutePath();

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

이 문제를 처리하는 다른 방법이 있으면 친절하게 공유하십시오. 감사


누가까지 작동하는 솔루션은 다음과 같습니다. stackoverflow.com/a/40205116/5002496
Gokul NC

Asus Zenfone 2에서 실제로 동일한 문제가 있지만 Dmitriy Lozenko에서 코드를 변경하여 문제를 해결합니다. stackoverflow.com/a/40582634/3940133
HendraWD

@HendraWD 당신이 제안하는 솔루션은 환경 변수에 대한 지원이 제거되어 Android Marshmallow (6.0)에서 작동하지 않을 수 있습니다. 정확히 기억할 수는 없지만 시도해 본 것 같습니다.
Abdul Rehman 2016

@GokulNC 나는 그것을 시도 할 것입니다. 합법적 인 것 같다 :)
Abdul Rehman

@GokulNC 예, 그래서 context.getExternalFilesDirs (null); 버전이> Marshmallow
HendraWD

0
String secStore = System.getenv("SECONDARY_STORAGE");

File externalsdpath = new File(secStore);

이것은 외부 SD 보조 스토리지의 경로를 가져옵니다.


0
//manifest file outside the application tag
//please give permission write this 
//<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        File file = new File("/mnt");
        String[] fileNameList = file.list(); //file names list inside the mnr folder
        String all_names = ""; //for the log information
        String foundedFullNameOfExtCard = ""; // full name of ext card will come here
        boolean isExtCardFounded = false;
        for (String name : fileNameList) {
            if (!isExtCardFounded) {
                isExtCardFounded = name.contains("ext");
                foundedFullNameOfExtCard = name;
            }
            all_names += name + "\n"; // for log
        }
        Log.d("dialog", all_names + foundedFullNameOfExtCard);

이것은 초보자가 적어도 장치에 탑재 된 모든 드라이버의 stringList를 얻는 방법입니다.
Emre Kilinc Arslan 2011

0

SD 카드의 파일에 액세스하려면 내 HTC One X (Android)에서 다음 경로를 사용합니다.

file:///storage/sdcard0/folder/filename.jpg

삼중 "/"에 주의 하십시오!


-1

Galaxy S3 Android 4.3에서 사용하는 경로는 ./storage/extSdCard/Card/ 이며 작업을 수행합니다. 도움이 되었기를 바랍니다.


-1

다음 단계가 저에게 효과적이었습니다. 다음 줄을 작성하면됩니다.

String sdf = new String(Environment.getExternalStorageDirectory().getName());
String sddir = new String(Environment.getExternalStorageDirectory().getPath().replace(sdf,""));

첫 번째 줄은 sd 디렉터리의 이름을 제공하며 두 번째 문자열의 replace 메서드에서 사용하면됩니다. 두 번째 문자열에는 내부 및 이동식 sd (/ storage / 내 경우) 경로가 포함됩니다 . 내 앱에이 경로가 필요했지만 필요한 경우 더 나아갈 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.