这就是我最终解决问题的方法。
首先调用一个意图让用户授予整个 SD 卡的写入权限。
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, 42);
然后在 onActivityResult 上获得写权限。
public void onActivityResult(int requestCode, int resultCode, Intent data) {
treeUri = data.getData();
final int takeFlags = data.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getActivity().getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
}
这是我用来将文件复制到 SD 卡的代码。
public static boolean copy(File copy, String directory, Context con) {
static FileInputStream inStream = null;
static OutputStream outStream = null;
DocumentFile dir= getDocumentFileIfAllowedToWrite(new File(directory), con);
String mime = mime(copy.toURI().toString());
DocumentFile copy1= dir.createFile(mime, copy.getName());
try {
inStream = new FileInputStream(copy);
outStream =
con.getContentResolver().openOutputStream(copy1.getUri());
byte[] buffer = new byte[16384];
int bytesRead;
while ((bytesRead = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inStream.close();
outStream.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
上面的代码使用了以下两种方法。
getDocumentFileIfAllowedToWrite 方法,如果允许写入则返回 DocumentFile。
public static DocumentFile getDocumentFileIfAllowedToWrite(File file, Context con) {
List<UriPermission> permissionUris = con.getContentResolver().getPersistedUriPermissions();
for (UriPermission permissionUri : permissionUris) {
Uri treeUri = permissionUri.getUri();
DocumentFile rootDocFile = DocumentFile.fromTreeUri(con, treeUri);
String rootDocFilePath = FileUtil.getFullPathFromTreeUri(treeUri, con);
if (file.getAbsolutePath().startsWith(rootDocFilePath)) {
ArrayList<String> pathInRootDocParts = new ArrayList<String>();
while (!rootDocFilePath.equals(file.getAbsolutePath())) {
pathInRootDocParts.add(file.getName());
file = file.getParentFile();
}
DocumentFile docFile = null;
if (pathInRootDocParts.size() == 0) {
docFile = DocumentFile.fromTreeUri(con, rootDocFile.getUri());
} else {
for (int i = pathInRootDocParts.size() - 1; i >= 0; i--) {
if (docFile == null) {
docFile = rootDocFile.findFile(pathInRootDocParts.get(i));
} else {
docFile = docFile.findFile(pathInRootDocParts.get(i));
}
}
}
if (docFile != null && docFile.canWrite()) {
return docFile;
} else {
return null;
}
}
}
return null;
}
以及返回文件的 mime 类型的 mime 方法。
public static String mime(String URI) {
static String type;
String extention = MimeTypeMap.getFileExtensionFromUrl(URI);
if (extention != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extention);
}
return type;
}
我已经用 Android Lollipop 和 Marshmallow 测试了上面的代码。
编辑:这是FileUtils类的链接。