有没有一种通用的方法来找到外置SD卡的位置?
请不要与外部存储混淆。
Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?
我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。
有没有一种通用的方法来找到外置SD卡的位置?
请不要与外部存储混淆。
Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?
我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。
当前回答
像Richard一样,我也使用/proc/mounts文件来获取可用的存储选项列表
public class StorageUtils {
private static final String TAG = "StorageUtils";
public static class StorageInfo {
public final String path;
public final boolean internal;
public final boolean readonly;
public final int display_number;
StorageInfo(String path, boolean internal, boolean readonly, int display_number) {
this.path = path;
this.internal = internal;
this.readonly = readonly;
this.display_number = display_number;
}
public String getDisplayName() {
StringBuilder res = new StringBuilder();
if (internal) {
res.append("Internal SD card");
} else if (display_number > 1) {
res.append("SD card " + display_number);
} else {
res.append("SD card");
}
if (readonly) {
res.append(" (Read only)");
}
return res.toString();
}
}
public static List<StorageInfo> getStorageList() {
List<StorageInfo> list = new ArrayList<StorageInfo>();
String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_internal = !Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState();
boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
BufferedReader buf_reader = null;
try {
HashSet<String> paths = new HashSet<String>();
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
String line;
int cur_display_number = 1;
Log.d(TAG, "/proc/mounts");
while ((line = buf_reader.readLine()) != null) {
Log.d(TAG, line);
if (line.contains("vfat") || line.contains("/mnt")) {
StringTokenizer tokens = new StringTokenizer(line, " ");
String unused = tokens.nextToken(); //device
String mount_point = tokens.nextToken(); //mount point
if (paths.contains(mount_point)) {
continue;
}
unused = tokens.nextToken(); //file system
List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
boolean readonly = flags.contains("ro");
if (mount_point.equals(def_path)) {
paths.add(def_path);
list.add(0, new StorageInfo(def_path, def_path_internal, readonly, -1));
} else if (line.contains("/dev/block/vold")) {
if (!line.contains("/mnt/secure")
&& !line.contains("/mnt/asec")
&& !line.contains("/mnt/obb")
&& !line.contains("/dev/mapper")
&& !line.contains("tmpfs")) {
paths.add(mount_point);
list.add(new StorageInfo(mount_point, false, readonly, cur_display_number++));
}
}
}
}
if (!paths.contains(def_path) && def_path_available) {
list.add(0, new StorageInfo(def_path, def_path_internal, def_path_readonly, -1));
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (buf_reader != null) {
try {
buf_reader.close();
} catch (IOException ex) {}
}
}
return list;
}
}
其他回答
Environment.getExternalStorageState()返回内部SD挂载点的路径,如"/mnt/sdcard"
不,Environment.getExternalStorageDirectory()指的是设备制造商认为是“外部存储”的任何东西。在某些设备上,这是可移动媒体,如SD卡。在某些设备上,这是设备上flash的一部分。这里的“外部存储”指的是“挂载在主机上通过USB大容量存储模式访问的东西”,至少对于Android 1来说是这样。X和2.x。
但问题是关于外置SD。如何获得像“/mnt/sdcard/external_sd”这样的路径(它可能因设备而异)?
Android没有“外部SD”的概念,除了如上所述的外部存储。
如果设备制造商已经选择将外部存储作为板载闪存,并且还具有SD卡,那么您将需要联系该制造商以确定是否可以使用SD卡(不保证)以及使用它的规则,例如使用它的路径。
更新
最近有两件事值得注意:
首先,在Android 4.4+上,你没有对可移动媒体(例如,“外部SD”)的写访问权,除非该媒体上的任何位置可能由getExternalFilesDirs()和getExternalCacheDirs()返回。请参阅Dave Smith对此的出色分析,特别是如果您想了解底层细节的话。
其次,为了避免有人质疑可移动媒体访问是否是Android SDK的一部分,以下是Dianne Hackborn的评估:
...keep in mind: until Android 4.4, the official Android platform has not supported SD cards at all except for two special cases: the old school storage layout where external storage is an SD card (which is still supported by the platform today), and a small feature added to Android 3.0 where it would scan additional SD cards and add them to the media provider and give apps read-only access to their files (which is also still supported in the platform today). Android 4.4 is the first release of the platform that has actually allowed applications to use SD cards for storage. Any access to them prior to that was through private, unsupported APIs. We now have a quite rich API in the platform that allows applications to make use of SD cards in a supported way, in better ways than they have been able to before: they can make free use of their app-specific storage area without requiring any permissions in the app, and can access any other files on the SD card as long as they go through the file picker, again without needing any special permissions.
你可以尝试使用支持库函数ContextCompat.getExternalFilesDirs():
final File[] appsDir=ContextCompat.getExternalFilesDirs(getActivity(),null);
final ArrayList<File> extRootPaths=new ArrayList<>();
for(final File file : appsDir)
extRootPaths.add(file.getParentFile().getParentFile().getParentFile().getParentFile());
第一个是主要的外部存储,其余的应该是真正的sd卡路径。
使用多个“.getParentFile()”的原因是要转到另一个文件夹,因为原始路径是
.../Android/data/YOUR_APP_PACKAGE_NAME/files/
编辑:这是一个更全面的方法,我已经创建,以获得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;
}
}
编辑:这里有更好的解决方案:
https://stackoverflow.com/a/27197248/878126
这么晚了,但最终我得到了一些东西,我已经测试了大多数设备(通过制造商和android版本),它在android 2.2+上工作。如果您发现它不起作用,请用您的设备名称注释它。我会解决的。如果有人感兴趣,我会解释它是如何工作的。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import android.util.Log;
/**
* @author ajeet
*05-Dec-2014 2014
*
*/
public class StorageUtil {
public boolean isRemovebleSDCardMounted() {
File file = new File("/sys/class/block/");
File[] files = file.listFiles(new MmcblkFilter("mmcblk\\d$"));
boolean flag = false;
for (File mmcfile : files) {
File scrfile = new File(mmcfile, "device/scr");
if (scrfile.exists()) {
flag = true;
break;
}
}
return flag;
}
public String getRemovebleSDCardPath() throws IOException {
String sdpath = null;
File file = new File("/sys/class/block/");
File[] files = file.listFiles(new MmcblkFilter("mmcblk\\d$"));
String sdcardDevfile = null;
for (File mmcfile : files) {
Log.d("SDCARD", mmcfile.getAbsolutePath());
File scrfile = new File(mmcfile, "device/scr");
if (scrfile.exists()) {
sdcardDevfile = mmcfile.getName();
Log.d("SDCARD", mmcfile.getName());
break;
}
}
if (sdcardDevfile == null) {
return null;
}
FileInputStream is;
BufferedReader reader;
files = file.listFiles(new MmcblkFilter(sdcardDevfile + "p\\d+"));
String deviceName = null;
if (files.length > 0) {
Log.d("SDCARD", files[0].getAbsolutePath());
File devfile = new File(files[0], "dev");
if (devfile.exists()) {
FileInputStream fis = new FileInputStream(devfile);
reader = new BufferedReader(new InputStreamReader(fis));
String line = reader.readLine();
deviceName = line;
}
Log.d("SDCARD", "" + deviceName);
if (deviceName == null) {
return null;
}
Log.d("SDCARD", deviceName);
final File mountFile = new File("/proc/self/mountinfo");
if (mountFile.exists()) {
is = new FileInputStream(mountFile);
reader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
// Log.d("SDCARD", line);
// line = reader.readLine();
// Log.d("SDCARD", line);
String[] mPonts = line.split("\\s+");
if (mPonts.length > 6) {
if (mPonts[2].trim().equalsIgnoreCase(deviceName)) {
if (mPonts[4].contains(".android_secure")
|| mPonts[4].contains("asec")) {
continue;
}
sdpath = mPonts[4];
Log.d("SDCARD", mPonts[4]);
}
}
}
}
}
return sdpath;
}
static class MmcblkFilter implements FilenameFilter {
private String pattern;
public MmcblkFilter(String pattern) {
this.pattern = pattern;
}
@Override
public boolean accept(File dir, String filename) {
if (filename.matches(pattern)) {
return true;
}
return false;
}
}
}
它适用于所有外部设备,但确保只获得外部设备文件夹名,然后你需要从给定的位置使用文件类获取文件。
public static List<String> getExternalMounts() {
final List<String> out = new ArrayList<>();
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;
}
调用:
List<String> list=getExternalMounts();
if(list.size()>0)
{
String[] arr=list.get(0).split("/");
int size=0;
if(arr!=null && arr.length>0) {
size= arr.length - 1;
}
File parentDir=new File("/storage/"+arr[size]);
if(parentDir.listFiles()!=null){
File parent[] = parentDir.listFiles();
for (int i = 0; i < parent.length; i++) {
// get file path as parent[i].getAbsolutePath());
}
}
}
访问外部存储
为了读写外部存储上的文件,你的应用程序必须获得READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE系统权限。例如:
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
我不知道为什么,但我需要在使用它之前在公共存储目录中创建的文件上调用. createnewfile()。在框架中,该方法的注释说它没有用。这是一个样本……
String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS) + File. txt文件。separator +“我的目录”; final File myDir = new File(myypath); 尝试{ myDir.mkdirs (); } catch(异常ex) { 吐司。makeText(this, "error: " + ex.getMessage(), Toast.LENGTH_LONG).show(); }
String fname = "whatever";
File newFile = new File(myDir, fname);
Log.i(TAG, "File exists --> " + newFile.exists()) //will be false
try {
if (newFile.createNewFile()) {
//continue
} else {
Log.e(TAG, "error creating file");
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}