无法在外部 SD 卡中创建目录 (Lollipop)
Can't create a directory in external sd card (Lollipop)
我对内部存储没有问题,但我想在外部 SD 卡上创建一个目录,对于 KitKat 之前的版本,File.mkdirs()
可以。
我的代码有一部分:
//external sd card
DocumentFile.fromFile(new File("/storage/sdcard1/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canRead(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canWrite(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/sdcard1/")).createDirectory("test"); //returns null
//internal storage
DocumentFile.fromFile(new File("/storage/emulated/0/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/emulated/0/")).createDirectory("test"); //it works
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).createFile("text", "file.txt"); //it works
//external sd card
(new File("/storage/sdcard1/test2/subfolder")).exists(); //returns false
(new File("/storage/sdcard1/test2/subfolder")).mkdirs(); //returns false
//internal storage
(new File("/storage/emulated/0/test2/subfolder")).exists(); //returns false
(new File("/storage/emulated/0/test2/subfolder")).mkdirs(); //returns true
在AndroidManifest.xml中:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
使用 BQ Aquaris e4、Android Lollipop 5.0 和 1GB 微型 SD 卡进行测试。
更新:这是我获取卷列表的方式:
private static ArrayList<StorageInfo> listAvaliableStorage(Context context) {
ArrayList<StorageInfo> storagges = new ArrayList<>();
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
try {
Class<?>[] paramClasses = {};
Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
getVolumeList.setAccessible(true);
Object[] params = {};
Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
if (invokes != null) {
StorageInfo info;
for (Object obj : invokes) {
Method getPath = obj.getClass().getMethod("getPath");
String path = (String) getPath.invoke(obj);
info = new StorageInfo(path);
File file = new File(info.getPath());
if ((file.exists()) && (file.isDirectory())
//&& (file.canWrite())
) {
info.setTotalStorage(file.getTotalSpace());
info.setFreeStorage(file.getUsableSpace());
Method isRemovable = obj.getClass().getMethod("isRemovable");
String state;
try {
Method getVolumeState = StorageManager.class.getMethod("getVolumeState", String.class);
state = (String) getVolumeState.invoke(storageManager, info.getPath());
info.setState(state);
info.setRemovable((Boolean) isRemovable.invoke(obj));
} catch (Exception e) {
e.printStackTrace();
}
storagges.add(info);
}
}
}
} catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
storagges.trimToSize();
return storagges;
}
硬编码路径仅适用于此示例
更新 2:
我用文件资源管理器在 SD 卡中创建了目录:"test"。当我执行这段代码时:
File file = new File("/storage/sdcard1/test/", "file.txt");
fileOutput = new FileOutputStream(file);
第二行抛出FileNotFoundException: /storage/sdcard1/test/file.txt: open failed: EACCES (Permission denied)
我做错了什么?
这是 Android Lollipop 的设计所致。
Environment.getExternalStorageDirectory()
将 return 仅模拟外部存储的路径。
Android 不会暴露任何给你 sdcard 路径的 API。
此外,在 Lollipop 中,除非通过存储访问框架获得用户许可,否则应用无法写入 SD 卡中其自身目录之外的位置。
尝试context.getExternalFilesDirs()
。这为您提供了一个路径列表,您的应用程序可以将数据写入这些路径。其中之一将在 sdcard 中,并且会像 /storage/sdcardname/Android/data/yourAppName/files
你必须明确定义位置,这是我制作的一个简短脚本,用于在该文件夹中创建一个文件夹,希望这对你有所帮助
import java.io.File;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;
public class DirectoryPicker extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chooser_list);//your xml layout file name
}
@Override
public void onStart() {
super.onStart();
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) { // **Check that we have access to create the folder(s)**
File Root = Environment.getExternalStorageDirectory(); //**Get the path and hold it**
//**Alternativly you can select a certain directory with**
//File Root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File folder = new File(Root.getAbsolutePath() + "/main_folder_to_add/second_folder/");//**This puts everything together CORRECTLY**
folder.mkdirs();//**Makes the directory folders, make sure to use mkdirs not mkdir**
if (!folder.exists()) {
folder.mkdirs();
}
Context context = getApplicationContext();//**Prove we were successful**
String msg = folder.toString();
Toast toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
toast.show();
return;
}
}
public void onBackPressed() {
finish();
}
}
我对内部存储没有问题,但我想在外部 SD 卡上创建一个目录,对于 KitKat 之前的版本,File.mkdirs()
可以。
我的代码有一部分:
//external sd card
DocumentFile.fromFile(new File("/storage/sdcard1/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canRead(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canWrite(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/sdcard1/")).createDirectory("test"); //returns null
//internal storage
DocumentFile.fromFile(new File("/storage/emulated/0/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/emulated/0/")).createDirectory("test"); //it works
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).createFile("text", "file.txt"); //it works
//external sd card
(new File("/storage/sdcard1/test2/subfolder")).exists(); //returns false
(new File("/storage/sdcard1/test2/subfolder")).mkdirs(); //returns false
//internal storage
(new File("/storage/emulated/0/test2/subfolder")).exists(); //returns false
(new File("/storage/emulated/0/test2/subfolder")).mkdirs(); //returns true
在AndroidManifest.xml中:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
使用 BQ Aquaris e4、Android Lollipop 5.0 和 1GB 微型 SD 卡进行测试。
更新:这是我获取卷列表的方式:
private static ArrayList<StorageInfo> listAvaliableStorage(Context context) {
ArrayList<StorageInfo> storagges = new ArrayList<>();
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
try {
Class<?>[] paramClasses = {};
Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
getVolumeList.setAccessible(true);
Object[] params = {};
Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
if (invokes != null) {
StorageInfo info;
for (Object obj : invokes) {
Method getPath = obj.getClass().getMethod("getPath");
String path = (String) getPath.invoke(obj);
info = new StorageInfo(path);
File file = new File(info.getPath());
if ((file.exists()) && (file.isDirectory())
//&& (file.canWrite())
) {
info.setTotalStorage(file.getTotalSpace());
info.setFreeStorage(file.getUsableSpace());
Method isRemovable = obj.getClass().getMethod("isRemovable");
String state;
try {
Method getVolumeState = StorageManager.class.getMethod("getVolumeState", String.class);
state = (String) getVolumeState.invoke(storageManager, info.getPath());
info.setState(state);
info.setRemovable((Boolean) isRemovable.invoke(obj));
} catch (Exception e) {
e.printStackTrace();
}
storagges.add(info);
}
}
}
} catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
storagges.trimToSize();
return storagges;
}
硬编码路径仅适用于此示例
更新 2: 我用文件资源管理器在 SD 卡中创建了目录:"test"。当我执行这段代码时:
File file = new File("/storage/sdcard1/test/", "file.txt");
fileOutput = new FileOutputStream(file);
第二行抛出FileNotFoundException: /storage/sdcard1/test/file.txt: open failed: EACCES (Permission denied)
我做错了什么?
这是 Android Lollipop 的设计所致。
Environment.getExternalStorageDirectory()
将 return 仅模拟外部存储的路径。
Android 不会暴露任何给你 sdcard 路径的 API。
此外,在 Lollipop 中,除非通过存储访问框架获得用户许可,否则应用无法写入 SD 卡中其自身目录之外的位置。
尝试context.getExternalFilesDirs()
。这为您提供了一个路径列表,您的应用程序可以将数据写入这些路径。其中之一将在 sdcard 中,并且会像 /storage/sdcardname/Android/data/yourAppName/files
你必须明确定义位置,这是我制作的一个简短脚本,用于在该文件夹中创建一个文件夹,希望这对你有所帮助
import java.io.File;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;
public class DirectoryPicker extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chooser_list);//your xml layout file name
}
@Override
public void onStart() {
super.onStart();
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) { // **Check that we have access to create the folder(s)**
File Root = Environment.getExternalStorageDirectory(); //**Get the path and hold it**
//**Alternativly you can select a certain directory with**
//File Root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File folder = new File(Root.getAbsolutePath() + "/main_folder_to_add/second_folder/");//**This puts everything together CORRECTLY**
folder.mkdirs();//**Makes the directory folders, make sure to use mkdirs not mkdir**
if (!folder.exists()) {
folder.mkdirs();
}
Context context = getApplicationContext();//**Prove we were successful**
String msg = folder.toString();
Toast toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
toast.show();
return;
}
}
public void onBackPressed() {
finish();
}
}