Làm cách nào để lấy đường dẫn thẻ SD bên ngoài cho Android 4.0+?


93

Samsung Galaxy S3 có một khe cắm thẻ nhớ SD bên ngoài, được gắn vào /mnt/extSdCard.

Làm thế nào tôi có thể đi được con đường này bằng một cái gì đó giống như Environment.getExternalStorageDirectory()?

Điều này sẽ trở lại mnt/sdcardvà tôi không thể tìm thấy API cho thẻ SD bên ngoài. (Hoặc bộ lưu trữ USB có thể tháo rời trên một số máy tính bảng.)


Tại sao bạn muốn điều này? Trên Android rất không thể làm được những việc như thế này. Có lẽ nếu bạn chia sẻ động lực của mình, chúng tôi có thể chỉ cho bạn hướng thực hành tốt nhất cho loại điều này.
rharter

2
Khi người dùng của bạn thay đổi điện thoại của họ, hãy cắm thẻ SD vào điện thoại mới và trên điện thoại đầu tiên là / sdcard, trên điện thoại thứ hai là / mnt / extSdCard, mọi thứ sử dụng đường dẫn tệp đều bị lỗi. Tôi cần tạo đường dẫn thực từ đường dẫn tương đối của nó.
Romulus Urakagi Ts'ai

Tôi bây giờ chủ đề này đã cũ nhưng điều này có thể giúp ích. bạn nên sử dụng phương pháp này. System.getenv (); xem dự án Môi trường3 để truy cập tất cả bộ nhớ được kết nối với thiết bị của bạn. github.com/omidfaraji/Enosystem3
Omid Faraji

Có thể có bản sao của Tìm vị trí thẻ SD bên ngoài
Brian Rogers

Đây là giải pháp của tôi hoạt động cho đến Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Câu trả lời:


58

Tôi có một biến thể về giải pháp mà tôi tìm thấy ở đây

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

Phương pháp ban đầu đã được thử nghiệm và làm việc với

  • Huawei X3 (còn hàng)
  • Galaxy S2 (còn hàng)
  • Galaxy S3 (còn hàng)

Tôi không chắc những phiên bản Android này đã được sử dụng khi chúng được thử nghiệm.

Tôi đã thử nghiệm phiên bản sửa đổi của mình với

  • Moto Xoom 4.1.2 (còn hàng)
  • Galaxy Nexus (cyanogenmod 10) sử dụng cáp otg
  • HTC Incredible (cyanogenmod 7.2) điều này trả lại cả bên trong và bên ngoài. Thiết bị này khá kỳ quặc ở chỗ bên trong của nó phần lớn không được sử dụng vì getExternalStorage () trả về một đường dẫn đến sdcard.

và một số thiết bị lưu trữ đơn sử dụng thẻ sdcard làm bộ nhớ chính

  • HTC G1 (cyanogenmod 6.1)
  • HTC G1 (còn hàng)
  • HTC Vision / G2 (còn hàng)

Ngoại trừ Incredible, tất cả các thiết bị này chỉ trả lại bộ nhớ di động của chúng. Có lẽ tôi nên thực hiện một số kiểm tra bổ sung, nhưng điều này ít nhất là tốt hơn một chút so với bất kỳ giải pháp nào mà tôi đã tìm thấy cho đến nay.


3
Tôi nghĩ rằng bạn giải pháp sẽ cho biết tên thư mục không hợp lệ nếu một gốc chứa chữ in hoa như / mnt / sdcard
Eugene Popovich

1
Tôi đã thử nghiệm trên một số thiết bị Motorola, Samsung và LG và nó hoạt động hoàn hảo.
pepedeutico 14/1013

2
@KhawarRaza, phương pháp này đã ngừng hoạt động kể từ kitkat. Sử dụng phương pháp của Dmitriy Lozenko, đây là phương pháp dự phòng nếu bạn đang hỗ trợ thiết bị pre-ics.
Gnathonic

1
Hoạt động trên HUAWEI Honor 3c. Cảm ơn.
li2

2
này đang trở lại / mnt thay vì / lưu trữ trên 4.0 + cho tôi trên Galaxy Note 3
ono

54

Tôi đã tìm thấy cách đáng tin cậy hơn để nhận đường dẫn đến tất cả các thẻ SD-CARD trong hệ thống. Điều này hoạt động trên tất cả các phiên bản Android và trả về đường dẫn đến tất cả các kho lưu trữ (bao gồm cả giả lập).

Hoạt động chính xác trên tất cả các thiết bị của tôi.

PS: Dựa trên mã nguồn của lớp Môi trường.

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 được cho là gì?
cYrixmorten

1
@cYrixmorten của nó được cung cấp trên đầu mã .. nó là Patterndành cho/
Ankit Bansal

Có ai đã thử nghiệm giải pháp này trên Lolipop không?
lenrok258

Tôi đã thử phương pháp này trên lenovo P780 nhưng không hoạt động.
Satheesh Kumar

2
Hoạt động lên đến Lollipop; không hoạt động trên Marshmallow (Galaxy Tab A w / 6.0.1) khi System.getenv("SECONDARY_STORAGE")trả về /storage/sdcard1. Vị trí đó không tồn tại, nhưng vị trí thực tế là /storage/42CE-FD0A, trong đó 42CE-FD0A là số sê-ri của phân vùng được định dạng.
DaveAlden

32

Tôi đoán để sử dụng thẻ sdcard bên ngoài, bạn cần sử dụng cái này:

new File("/mnt/external_sd/")

HOẶC LÀ

new File("/mnt/extSdCard/")

trong trường hợp của bạn...

thay thế cho Environment.getExternalStorageDirectory()

Làm việc cho tôi. Bạn nên kiểm tra những gì trong thư mục mnt trước và làm việc từ đó ..


Bạn nên sử dụng một số loại phương pháp lựa chọn để chọn thẻ sdcard sẽ sử dụng:

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

1
Trên thực tế, tôi muốn một phương thức hơn là đường dẫn mã cứng, nhưng / mnt / list sẽ ổn. Cảm ơn bạn.
Romulus Urakagi Ts'ai

Không vấn đề gì. Có một tùy chọn trong cài đặt mà bạn chọn đường dẫn từ đó, sau đó sử dụng phương pháp để lấy phần này.
FabianCook

10
Rất tiếc, thẻ SD bên ngoài có thể nằm trong một thư mục khác với / mnt. Ví dụ như trên Samsung GT-I9305 đó là lưu trữ / / extSdCard trong khi thẻ SD trong xây dựng có path / lưu trữ / sdcard0
iseeall

2
Sau đó, bạn sẽ nhận được vị trí sdcard, sau đó lấy thư mục mẹ của nó.
FabianCook

Đường dẫn của thẻ SD bên ngoài (đối với ổ đĩa kết nối với máy tính bảng qua cáp OTG) cho Android 4.0+ sẽ như thế nào?
osimer pothe

15

Để truy xuất tất cả các Bộ nhớ ngoài (cho dù chúng là thẻ SD hay bộ lưu trữ bên trong không thể tháo rời ), bạn có thể sử dụng mã sau:

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

Ngoài ra, bạn có thể sử dụng System.getenv ("EXTERNAL_STORAGE") để truy xuất thư mục Bộ nhớ ngoài chính (ví dụ: "/ storage / sdcard0" ) và System.getenv ("SECONDARY_STORAGE") để lấy lại danh sách tất cả các thư mục phụ (ví dụ: " / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB " ). Hãy nhớ rằng, cũng trong trường hợp này, bạn có thể muốn lọc danh sách các thư mục phụ để loại trừ các ổ USB.

Trong mọi trường hợp, xin lưu ý rằng việc sử dụng các đường dẫn được mã hóa cứng luôn là một cách tiếp cận không tốt (đặc biệt khi mọi nhà sản xuất có thể thay đổi nó theo ý muốn).


System.getenv ("SECONDARY_STORAGE") KHÔNG hoạt động đối với các thiết bị USB được gắn qua cáp OTG trên Nexus 5 và Nexus 7.
Khawar Raza

Đây là giải pháp của tôi hoạt động cho đến Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

14

Tôi đang sử dụng giải pháp của Dmitriy Lozenko cho đến khi tôi kiểm tra trên Asus Zenfone2 , Marshmallow 6.0.1 và giải pháp không hoạt động. Giải pháp không thành công khi nhận được EMULATED_STORAGE_TARGET , đặc biệt cho đường dẫn microSD, tức là: / storage / F99C-10F4 / . Tôi đã chỉnh sửa mã để lấy các đường dẫn gốc được mô phỏng trực tiếp từ các đường dẫn ứng dụng được mô phỏng context.getExternalFilesDirs(null);và thêm các đường dẫn vật lý cụ thể dành cho mô hình điện thoại đã biết .

Để làm cho cuộc sống của chúng tôi dễ dàng hơn, tôi đã tạo một thư viện ở đây . Bạn có thể sử dụng nó thông qua hệ thống xây dựng gradle, maven, sbt và leiningen.

Nếu bạn thích cách cũ, bạn cũng có thể sao chép, dán tệp trực tiếp từ đây , nhưng bạn sẽ không biết liệu có bản cập nhật nào trong tương lai nếu không kiểm tra thủ công.

Nếu bạn có bất kỳ câu hỏi hoặc gợi ý nào, vui lòng cho tôi biết


1
Tôi đã không sử dụng nó rộng rãi, nhưng nó hoạt động tốt cho vấn đề của tôi.
David

1
Giải pháp tuyệt vời! Tuy nhiên, có một lưu ý - nếu thẻ SD được tháo ra khi ứng dụng đang hoạt động, context.getExternalFilesDirs(null)sẽ trả về nullmột trong các tệp và sẽ có ngoại lệ trong vòng lặp sau. Tôi đề nghị thêm if (file == null) continue;làm dòng đầu tiên trong vòng lặp.
Koger

1
@Koger cảm ơn bạn đã đề xuất, tôi đã thêm nó và sửa lỗi chính tả cho câu trả lời của mình
HendraWD

13

Tin tốt! Trong KitKat hiện có một API công khai để tương tác với các thiết bị lưu trữ dùng chung thứ cấp này.

Phương thức Context.getExternalFilesDirs()và mới Context.getExternalCacheDirs()có thể trả về nhiều đường dẫn, bao gồm cả thiết bị chính và thiết bị phụ. Sau đó, bạn có thể lặp lại chúng và kiểm tra Environment.getStorageState()File.getFreeSpace()xác định nơi tốt nhất để lưu trữ tệp của mình. Các phương pháp này cũng có sẵn ContextCompattrong thư viện support-v4.

Cũng lưu ý rằng nếu bạn chỉ quan tâm đến việc sử dụng các thư mục được trả về Context, bạn không cần READ_hoặc WRITE_EXTERNAL_STORAGEquyền nữa. Về sau, bạn sẽ luôn có quyền truy cập đọc / ghi vào các thư mục này mà không yêu cầu quyền bổ sung.

Các ứng dụng cũng có thể tiếp tục hoạt động trên các thiết bị cũ hơn bằng cách yêu cầu cấp phép của chúng như sau:

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

Cảm ơn bạn. Tôi đã tạo một số mã theo những gì bạn đã viết và tôi nghĩ rằng nó hoạt động tốt trong việc tìm kiếm tất cả các đường dẫn thẻ sd. Bạn có thể xem một lần một lần được không ? Tại đây: stackoverflow.com/a/27197248/878126. BTW, bạn không nên sử dụng "getFreeSpace" vì về mặt lý thuyết, không gian trống có thể thay đổi trong thời gian chạy.
nhà phát triển android


10

Tôi đã làm như sau để nhận được acces cho tất cả các thẻ sd bên ngoài.

Với:

File primaryExtSd=Environment.getExternalStorageDirectory();

bạn nhận được đường dẫn đến SD bên ngoài chính Sau đó với:

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

bạn nhận được dir cha của bộ nhớ ngoài chính và nó cũng là cha của tất cả sd bên ngoài. Bây giờ, bạn có thể liệt kê tất cả các bộ nhớ và chọn bộ mà bạn muốn.

Hy vọng nó là hữu ích.


Đây phải là câu trả lời được chấp nhận, và cảm ơn vì nó hữu ích.
Meanman

9

Đây là cách tôi lấy danh sách các đường dẫn thẻ SD (không bao gồm bộ nhớ ngoài chính):

  /**
   * 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()Đây là một công thức cho thảm họa. Nếu bạn đang làm điều này, bạn cũng có thể chỉ cần mã hóa đường dẫn mà bạn muốn, nếu bạn nhận được một bộ đệm ẩn với bất kỳ thứ gì khác ngoài 4 cha mẹ, bạn sẽ nhận được NullPointerException hoặc đường dẫn sai.
rharter

@rharter Đúng. Đã khắc phục sự cố này. Mời các bạn xem qua.
nhà phát triển android

Tài liệu ( developer.android.com/reference/android/content/… ) cho biết "Các thiết bị lưu trữ bên ngoài được trả lại tại đây được coi là một phần vĩnh viễn của thiết bị, bao gồm cả bộ nhớ ngoài giả lập và các khe cắm phương tiện vật lý, chẳng hạn như thẻ SD trong pin Các đường dẫn trả về không bao gồm các thiết bị tạm thời, chẳng hạn như ổ đĩa flash USB. " Có vẻ như chúng cũng không bao gồm khe cắm thẻ SD, tức là những nơi bạn có thể tháo thẻ SD mà không cần đi dưới pin. Tuy nhiên, khó nói.
LarsH

1
@LarsH Chà, tôi đã tự kiểm tra nó trên SGS3 (và thẻ SD thực), vì vậy tôi nghĩ nó cũng sẽ hoạt động trên các thiết bị khác.
nhà phát triển android

5

Cảm ơn các manh mối do các bạn cung cấp, đặc biệt là @SmartLemon, tôi đã có giải pháp. Trong trường hợp ai đó cần nó, tôi đặt giải pháp cuối cùng của mình ở đây (để tìm thẻ SD bên ngoài được liệt kê đầu tiên):

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

Nếu không có thẻ SD bên ngoài ở đó, nó sẽ trả về bộ nhớ trên bo mạch. Tôi sẽ sử dụng nó nếu sdcard không tồn tại, bạn có thể cần phải thay đổi nó.


1
điều này ném null trên Phiên bản Android 19. rootDir.listFiles () return null. Tôi đã kiểm tra nó với nexus 7, giả lập và galaxy note.
Manu Zi

3

tham khảo mã của tôi, hy vọng hữu ích cho bạn:

    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

Trên thực tế, trong một số thiết bị, tên mặc định sdcard bên ngoài được hiển thị extSdCardvà đối với các thiết bị khác thì như vậy sdcard1.

Đoạn mã này giúp tìm ra đường dẫn chính xác đó và giúp truy xuất cho bạn đường dẫn của thiết bị bên ngoài.

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

Điều này đã giúp tôi rất nhiều.
Droid Chris

Có một số thiết bị sử dụng "/ storage / external_sd" (LG G3 KitKat), Marshmallow có một số sơ đồ khác nhau, Bộ nhớ trong trên Nexus 5X sử dụng Android 6.0 là "/ mnt / sdcard" và SDcard bên ngoài được tìm thấy trong "/ storage / XXXX-XXXX "
AB

2

Đúng. Các nhà sản xuất khác nhau sử dụng tên thẻ SDcard khác nhau như trong Samsung Tab 3 extsd của nó và các thiết bị samsung khác sử dụng thẻ nhớ như thế này, nhà sản xuất khác sử dụng các tên khác nhau.

Tôi đã có yêu cầu tương tự như bạn. vì vậy tôi đã tạo một ví dụ mẫu cho bạn từ dự án của tôi đi tới liên kết này Ví dụ về trình chọn thư mục Android sử dụng thư viện androi-dirchooser. Ví dụ này phát hiện thẻ SDcard và liệt kê tất cả các thư mục con và nó cũng phát hiện nếu thiết bị có nhiều hơn một thẻ SD.

Một phần của mã trông giống như thế này. Ví dụ đầy đủ, hãy truy cập liên kết 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

Giải pháp này (được tập hợp từ các câu trả lời khác cho câu hỏi này) xử lý một thực tế (như được đề cập bởi @ono) System.getenv("SECONDARY_STORAGE")không có ích với Marshmallow.

Đã thử nghiệm và làm việc trên:

  • Samsung Galaxy Tab 2 (Android 4.1.1 - Cổ phiếu)
  • Samsung Galaxy Note 8.0 (Android 4.2.2 - Cổ phiếu)
  • Samsung Galaxy S4 (Android 4.4 - Cổ phiếu)
  • Samsung Galaxy S4 (Android 5.1.1 - Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1 - Cổ phiếu)

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

Điều kiện rawSecondaryStoragesStr.contains(path)có thể cho phép bộ nhớ trong cũng được thêm vào; tốt hơn chúng ta có thể loại bỏ điều kiện đó .. Tôi gặp phải vấn đề đó, vì thiết bị của tôi trả về thư mục lưu trữ nội bộ với System.getenv("SECONDARY_STORAGE"), nhưng trả về đường dẫn Thẻ nhớ ngoài với System.getenv(EXTERNAL_STORAGE)trên KitKat; có vẻ như các nhà cung cấp khác nhau đã thực hiện điều này cách khác nhau mà không cần bất kỳ tiêu chuẩn chết tiệt ..
Gokul NC

Đây là giải pháp của tôi hoạt động cho đến Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

1

Trên một số thiết bị (ví dụ như samsung galaxy sII) thẻ nhớ trong có thể ở dạng vfat. Trong trường hợp này, hãy tham khảo mã cuối cùng, chúng tôi nhận được đường dẫn thẻ nhớ trong (/ mnt / sdcad) nhưng không có thẻ ngoài. Mã tham khảo dưới đây giải quyết vấn đề này.

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

Tôi chắc chắn mã này chắc chắn sẽ giải quyết được sự cố của bạn ... Điều này đang hoạt động tốt đối với tôi ... \

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 () sẽ không cung cấp cho bạn vị trí chính xác của vị trí sdcard bên ngoài hoặc vị trí usb của bạn. Bạn chỉ cần phân tích cú pháp tới vị trí "/ proc / mounts" sau đó / dev / fuse / storage / sdcard1 "sẽ cung cấp cho bạn vị trí sdcard của bạn có đúng không chưa gắn hoặc không tương tự cho cách usb also.This bạn có thể dễ dàng có được it..Cheers..Sam
Sam

0

Đo không phải sự thật. / mnt / sdcard / external_sd có thể tồn tại ngay cả khi thẻ SD không được gắn. ứng dụng của bạn sẽ gặp sự cố khi bạn cố gắng ghi vào / mnt / sdcard / external_sd khi nó chưa được gắn kết.

trước tiên bạn cần kiểm tra xem thẻ SD đã được lắp hay chưa bằng cách sử dụng:

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

7
getExternalStorageState()Tôi nghĩ rằng điều này là vô nghĩa khi trả lại bộ nhớ trong thay vì thẻ SD bên ngoài.
Romulus Urakagi Ts'ai

Hiện họ có cung cấp chức năng lấy thẻ SD bên ngoài không? Tôi không thể tìm thấy bất kỳ manh mối nào sau khi tìm kiếm xung quanh một giờ. Nếu đúng như vậy thì điều này thật lạ sau rất nhiều phiên bản cập nhật.
rml

Khi bạn nói "điều đó không đúng", bạn đang ám chỉ điều gì? Tôi biết, đó là 2,5 năm trước ...
LarsH

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

4
Nó không hoạt động. Nó trả về đường dẫn của Bộ nhớ trong. tức là / lưu trữ / giả lập / 0

0

Bạn có thể sử dụng một cái gì đó như - Context.getExternalCacheDirs () hoặc Context.getExternalFilesDirs () hoặc Context.getObbDirs (). Chúng cung cấp các thư mục cụ thể cho ứng dụng trong tất cả các thiết bị lưu trữ bên ngoài nơi ứng dụng có thể lưu trữ các tệp của nó.

Vì vậy, một cái gì đó như thế này - Context.getExternalCacheDirs () [i] .getParentFile (). GetParentFile (). GetParentFile (). GetParent () có thể cung cấp cho bạn đường dẫn gốc của các thiết bị lưu trữ bên ngoài.

Tôi biết các lệnh này dành cho một mục đích khác nhưng các câu trả lời khác không phù hợp với tôi.

Liên kết này đã cho tôi những gợi ý tốt - https://possiblemobile.com/2014/03/android-external-storage/


0

System.getenv("SECONDARY_STORAGE")trả về null cho Marshmallow. Đây là một cách khác để tìm tất cả các dirs bên ngoài. Bạn có thể kiểm tra xem nó có thể tháo rời hay không, từ đó xác định xem nội bộ / bên ngoài

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

Tôi đã thử các giải pháp do Dmitriy LozenkoGnathonic cung cấp trên Samsung Galaxy Tab S2 (Model: T819Y) của mình nhưng không có giải pháp nào giúp tôi truy xuất đường dẫn đến thư mục Thẻ SD bên ngoài. mountthực thi lệnh chứa đường dẫn bắt buộc đến thư mục Thẻ SD bên ngoài (tức là / Storage / A5F9-15F4) nhưng nó không khớp với biểu thức chính quy do đó nó không được trả về. Tôi không nhận được cơ chế đặt tên thư mục theo sau của Samsung. Tại sao họ lại đi lệch khỏi các tiêu chuẩn (ví dụ: thẻ extsdcard) và đưa ra một thứ gì đó thực sự khó hiểu như trong trường hợp của tôi (ví dụ / Storage / A5F9-15F4) . Có điều gì tôi đang thiếu không? Dù sao, sau những thay đổi trong biểu thức chính quy của Gnathonic giải pháp đã giúp tôi có được thư mục sdcard hợp lệ:

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;

Tôi không chắc liệu đây có phải là giải pháp hợp lệ hay không và liệu nó có mang lại kết quả cho các máy tính bảng Samsung khác hay không nhưng hiện tại nó đã khắc phục được sự cố của tôi. Sau đây là một phương pháp khác để truy xuất đường dẫn Thẻ SD di động trong Android (v6.0). Tôi đã thử nghiệm phương pháp với android marshmallow và nó hoạt động. Phương pháp tiếp cận được sử dụng trong nó là rất cơ bản và chắc chắn sẽ hoạt động cho các phiên bản khác nhưng việc kiểm tra là bắt buộc. Một số thông tin chi tiết về nó sẽ hữu ích:

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

Vui lòng chia sẻ nếu bạn có bất kỳ cách tiếp cận nào khác để xử lý vấn đề này. Cảm ơn


Đây là giải pháp của tôi hoạt động cho đến Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Tôi thực sự có vấn đề tương tự với bạn về Asus Zenfone 2, nhưng tôi thay đổi mã từ Dmitriy Lozenko thay vì giải quyết vấn đề stackoverflow.com/a/40582634/3940133
HendraWD

@HendraWD giải pháp mà bạn đang đề xuất có thể không hoạt động với Android Marshmallow (6.0) vì hỗ trợ cho các biến môi trường bị loại bỏ trong đó. Tôi không thể nhớ chính xác nhưng tôi nghĩ tôi đã thử.
Abdul Rehman

@GokulNC Tôi sẽ thử. có vẻ hợp pháp :)
Abdul Rehman

@GokulNC vâng, đó là lý do tại sao tôi sử dụng context.getExternalFilesDirs (null); thay vì trực tiếp sử dụng các đường dẫn vật lý khi phiên bản> Marshmallow
HendraWD

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

File externalsdpath = new File(secStore);

Điều này sẽ nhận được đường dẫn của bộ nhớ phụ sd bên ngoài.


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

Đây là cách khác dành cho người mới bắt đầu ít nhất là nhận được stringList của tất cả các trình điều khiển được gắn trong thiết bị.
Emre Kilinc Arslan

0

Để truy cập các tệp trong thẻ SD của tôi , trên HTC One X (Android), tôi sử dụng đường dẫn sau:

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

Lưu ý dấu ba chấm "/"!


-1

Trên Galaxy S3 Android 4.3, đường dẫn tôi sử dụng là ./storage/extSdCard/Card/ và nó thực hiện công việc. Hy vọng nó giúp,


-1

Các bước sau đã làm việc cho tôi. Bạn chỉ cần viết những dòng này:

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

Dòng đầu tiên sẽ cung cấp tên của thư mục sd và bạn chỉ cần sử dụng nó trong phương thức thay thế cho chuỗi thứ hai. Chuỗi thứ hai sẽ chứa đường dẫn cho sd nội bộ và di động (/ lưu trữ / trong trường hợp của tôi) . Tôi chỉ cần đường dẫn này cho ứng dụng của mình nhưng bạn có thể đi xa hơn nếu cần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.