我正在尝试找到一种方法,该方法可以处理除媒体文件(图片/视频/音频)以外的任何文件的创建和复制,以将其从Android Q的内部存储中的一个位置复制到另一个位置。我的应用文件夹,我希望这些文件夹移到“下载文件夹”或我可以在内部存储中创建的某个目录,然后移动它们。
我搜索并发现下面的代码经过修改,但是缺少一些使之可行的东西。可以帮忙吗。
ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "sam.txt");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues);
try {
InputStream inputStream = contentResolver.openInputStream(uri);
OutputStream outputStream = new FileOutputStream(Environment.DIRECTORY_DOWNLOADS+"/");
byte[] buffer = new byte[1024];
int length;
//copy the file content in bytes
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
以上是完整代码,因为我收到错误消息“未知URL”。缺什么?请帮忙。
答案 0 :(得分:3)
1。创建和写入文件
createAndWriteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "menuCategory"); //file name
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain"); //file extension, will automatically add to file
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS + "/Kamen Rider Decade/"); //end "/" is not mandatory
Uri uri = getContentResolver().insert(MediaStore.Files.getContentUri("external"), values); //important!
OutputStream outputStream = getContentResolver().openOutputStream(uri);
outputStream.write("This is menu category data.".getBytes());
outputStream.close();
Toast.makeText(view.getContext(), "File created successfully", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(view.getContext(), "Fail to create file", Toast.LENGTH_SHORT).show();
}
}
});
2。查找和读取文件
findAndReadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri contentUri = MediaStore.Files.getContentUri("external");
String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";
String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/Kamen Rider Decade/"};
Cursor cursor = getContentResolver().query(contentUri, null, selection, selectionArgs, null);
Uri uri = null;
if (cursor.getCount() == 0) {
Toast.makeText(view.getContext(), "No file found in \"" + Environment.DIRECTORY_DOCUMENTS + "/Kamen Rider Decade/\"", Toast.LENGTH_LONG).show();
} else {
while (cursor.moveToNext()) {
String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
if (fileName.equals("menuCategory.txt")) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
uri = ContentUris.withAppendedId(contentUri, id);
break;
}
}
if (uri == null) {
Toast.makeText(view.getContext(), "\"menuCategory.txt\" not found", Toast.LENGTH_SHORT).show();
} else {
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
int size = inputStream.available();
byte[] bytes = new byte[size];
inputStream.read(bytes);
inputStream.close();
String jsonString = new String(bytes, StandardCharsets.UTF_8);
AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
builder.setTitle("File Content");
builder.setMessage(jsonString);
builder.setPositiveButton("OK", null);
builder.create().show();
} catch (IOException e) {
Toast.makeText(view.getContext(), "Fail to read file", Toast.LENGTH_SHORT).show();
}
}
}
}
});
3。查找并覆盖文件
findAndWriteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri contentUri = MediaStore.Files.getContentUri("external");
String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";
String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/Kamen Rider Decade/"}; //must include "/" in front and end
Cursor cursor = getContentResolver().query(contentUri, null, selection, selectionArgs, null);
Uri uri = null;
if (cursor.getCount() == 0) {
Toast.makeText(view.getContext(), "No file found in \"" + Environment.DIRECTORY_DOCUMENTS + "/Kamen Rider Decade/\"", Toast.LENGTH_LONG).show();
} else {
while (cursor.moveToNext()) {
String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
if (fileName.equals("menuCategory.txt")) { //must include extension
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
uri = ContentUris.withAppendedId(contentUri, id);
break;
}
}
if (uri == null) {
Toast.makeText(view.getContext(), "\"menuCategory.txt\" not found", Toast.LENGTH_SHORT).show();
} else {
try {
OutputStream outputStream = getContentResolver().openOutputStream(uri, "rwt"); //overwrite mode, see below
outputStream.write("This is overwritten data。\n你就不要想起我。".getBytes());
outputStream.close();
Toast.makeText(view.getContext(), "File written successfully", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(view.getContext(), "Fail to write file", Toast.LENGTH_SHORT).show();
}
}
}
}
});
演示:https://www.youtube.com/watch?v=idsUMiWjfnM
希望这对您有所帮助。
答案 1 :(得分:0)
您提到的Environment.getExternalStoragePublicDirectory
已被标记为已弃用。因此,没有常规方法可以获取“下载”目录的路径来将文件保存在那里。另外,您可以使用ACTION_CREATE_DOCUMENT
显示路径选择器,然后使用返回的uri将文件写入所选位置。
这是显示选择器的方法:
// Request code for creating a document.
const val CREATE_FILE = 1
private fun createFile(pickerInitialUri: Uri) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, "sam.txt")
// Optionally, specify a URI for the directory that should be opened in
// the system file picker before your app creates the document.
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, CREATE_FILE)
}
这是选择uri并写入文件的方法:
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == CREATE_FILE && resultCode == Activity.RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
resultData?.data?.also { outputUri ->
// Perform operations on the document using its URI.
FileInputStream(inputFile).use { inputStream ->
context.contentResolver.openFileDescriptor(outputUri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { outputStream ->
FileUtils.copy(inputStream, outputStream)
}
}
}
}
}
}
更多信息here。
编辑:
要选择一个目录来持久保存文件,可以使用ACTION_OPEN_DOCUMENT_TREE
。然后使用takePersistableUriPermission
方法获得授予的持久权限,以便在设备重新启动后使用它。然后使用DocumentFile
执行文件操作。
打开目录请求:
private static final int OPEN_DIRECTORY_REQUEST_CODE = 1;
void openDirectory() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
}
接收选择的目录并获得持久权限:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OPEN_DIRECTORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri directoryUri = data.getData();
if (directoryUri == null)
return;
requireContext()
.getContentResolver()
.takePersistableUriPermission(directoryUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// persist picked uri to be able to reuse it later
} else
super.onActivityResult(requestCode, resultCode, data);
}
最后保存文件:
private void persistFile(@NonNull Uri directoryUri,
@NonNull File fileToPersist,
@NonNull String mimeType,
@NonNull String displayName) {
DocumentFile dirFile = DocumentFile.fromSingleUri(requireContext(), directoryUri);
if (dirFile != null) {
DocumentFile file = dirFile.createFile(mimeType, displayName);
if (file != null) {
Uri outputUri = file.getUri();
try (ParcelFileDescriptor fd = requireContext().getContentResolver().openFileDescriptor(outputUri, "w")) {
if (fd != null) {
try (FileInputStream inputStream = new FileInputStream(fileToPersist)) {
try (FileOutputStream outputStream = new FileOutputStream(fd.getFileDescriptor())) {
FileUtils.copy(inputStream, outputStream);
}
}
}
} catch (Throwable th) {
th.printStackTrace();
}
}
}
}
查看this repo以获取ACTION_CREATE_DOCUMENT
用法的示例。