在 Android 中实现文件选取器并将所选文件复制到另一个位置
Implementing a File Picker in Android and copying the selected file to another location
我正在尝试在我的 Android 项目中实现文件选择器。到目前为止我能做的是:
Intent chooseFile;
Intent intent;
chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
intent = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(intent, PICKFILE_RESULT_CODE);
然后在我的 onActivityResult()
switch(requestCode){
case PICKFILE_RESULT_CODE:
if(resultCode==-1){
Uri uri = data.getData();
String filePath = uri.getPath();
Toast.makeText(getActivity(), filePath,
Toast.LENGTH_LONG).show();
}
break;
}
这是打开文件选择器,但这不是我想要的。例如,我想select一个文件(.txt),然后得到那个File
然后使用它。使用这段代码,我以为我会得到 完整路径 但它没有发生;例如我得到:/document/5318/
。但是通过这条路径我无法获取文件。我创建了一个名为 PathToFile()
的方法 returns a File
:
private File PathToFile(String path) {
File tempFileToUpload;
tempFileToUpload = new File(path);
return tempFileToUpload;
}
我想做的是让用户从任何地方选择 File
表示 DropBox
、Drive
、SDCard
、Mega
、等等...我找不到正确的方法,我试图获得 Path
然后通过此 Path
获得 File
... 但它没有工作,所以我认为最好获取 File
本身,然后以编程方式使用此 File
我 Copy
此或 Delete
.
编辑(当前代码)
我的Intent
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);
我有一个问题,因为我不知道 text/plain
支持什么,但我会调查一下,但目前这并不重要。
在我的 onActivityResult()
上,我使用了与 相同的方法,但我不知道是否可以使用它 Copy
这个 File
到另一个部分来自我的 SDcard
我正在等待他的回答。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
//get the path
Log.d("Path???", content_describer.getPath());
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getActivity().getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
text.setText(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
getPath()
来自@Y.S。
我正在这样做:
String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getActivity().getContentResolver().query(content_describer, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
cursor.close();
Log.d( "PATH-->",cursor.getString(column_index));
得到 NullPointerException
:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=131073, result=-1, data=Intent { dat=file:///path typ=text/plain flg=0x3 }} to activity {info.androidhive.tabsswipe/info.androidhive.tabsswipe.MainActivity2}: java.lang.NullPointerException
使用代码进行编辑感谢 @Y.S., @Lukas Knuth, and @CommonsWare。
这是Intent
,我只接受文件text/plain
。
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);
在我的 onActivityResult()
上,我创建了一个 URI
来获取 Intent
的数据,我创建了一个 File
来保存 绝对路径 做 content_describer.getPath();
,然后我保留路径名称以在 TextView
和 content_describer.getLastPathSegment();
中使用它(那太棒了 @Y.S。不知道那个函数),我创建了第二个 File
,我称之为 destination
,然后我发送 AbsolutePath
来创建这个 File
.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
String src = content_describer.getPath();
source = new File(src);
Log.d("src is ", source.toString());
String filename = content_describer.getLastPathSegment();
text.setText(filename);
Log.d("FileName is ",filename);
destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Test/TestTest/" + filename);
Log.d("Destination is ", destination.toString());
SetToFolder.setEnabled(true);
}
}
我还创建了一个函数,您必须发送我们之前创建的 source file
和 destination file
以将其复制到新文件夹。
private void copy(File source, File destination) throws IOException {
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();
try {
in.transferTo(0, in.size(), out);
} catch(Exception e){
Log.d("Exception", e.toString());
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
我还创建了一个函数,告诉我这个文件夹是否存在(我必须发送 destination file
,如果它不存在,我创建这个文件夹,如果不存在,我不要什么都不做。
private void DirectoryExist (File destination) {
if(!destination.isDirectory()) {
if(destination.mkdirs()){
Log.d("Carpeta creada","....");
}else{
Log.d("Carpeta no creada","....");
}
}
再次感谢您的帮助,希望您喜欢这段与大家一起制作的代码:)
A Uri
is not a file。 Uri
更接近 Web 服务器 URL。这是一个不透明的地址,仅对 "server"(或在本例中为 ContentProvider
)有意义。
正如您使用 InputStream
读取由 Web URL 表示的字节一样,您使用 InputStream
读取由 [=10= 表示的字节].您可以通过在 ContentResolver
.
上调用 openInputStream()
来获得这样的流
正如 @CommonsWare 已经指出的,Android returns 你 Uri
,这是一个比文件路径更抽象的概念.
它也可以描述一个简单的文件路径,但它也可以描述通过通过一个应用程序(如content://media/external/audio/media/710
)访问的资源。
如果您希望您的用户从 phone 中选择任何文件以从您的应用程序中读取它,您可以通过请求文件(正如您所做的那样)然后使用 ContentResolver
以获得选择器返回的 Uri
的 InputStream
。
这是一个例子:
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
// Ask specifically for something that can be opened:
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("*/*");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_REQUEST_CODE
);
// And then somewhere, in your activity:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_REQUEST_CODE && resultCode == RESULT_OK){
Uri content_describer = data.getData();
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
some_view.setText(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
重要提示:一些提供商(如 Dropbox)store/cache 将他们的数据存储在外部存储中。您需要在清单中声明 android.permission.READ_EXTERNAL_STORAGE
-permission,否则您将获得 FileNotFoundException
,即使文件在那里。
更新:是的,您可以通过从一个流读取文件并将其写入另一个流来复制文件:
// Error handling is omitted for shorter code!
Uri content_describer = data.getData();
InputStream in = null;
OutputStream out = null;
try {
// open the user-picked file for reading:
in = getContentResolver().openInputStream(content_describer);
// open the output-file:
out = new FileOutputStream(new File("some/path/to/a/writable/file"));
// copy the content:
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
// Contents are copied!
} finally {
if (in != null) {
in.close();
}
if (out != null){
out.close();
}
}
可能无法删除该文件,因为该文件不属于您,它属于与您共享它的应用程序。因此,所属应用程序负责删除该文件。
第 1 步 - 使用隐式 Intent
:
要从设备中选择文件,您应该使用隐式 Intent
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(chooseFile, PICKFILE_RESULT_CODE);
第 2 步 - 获取绝对文件路径:
要从 Uri
获取文件路径,请首先尝试使用
Uri uri = data.getData();
String src = uri.getPath();
其中 data
是 onActivityResult()
中返回的 Intent
。
如果这不起作用,请使用以下方法:
public String getPath(Uri uri) {
String path = null;
String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if(cursor == null){
path = uri.getPath()
}
else{
cursor.moveToFirst();
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
path = cursor.getString(column_index);
cursor.close();
}
return ((path == null || path.isEmpty()) ? (uri.getPath()) : path);
}
这两种方法中至少有一种应该可以为您提供正确的完整路径。
第 3 步 - 复制文件:
我相信您想要的是将文件从一个位置复制到另一个位置。
为此,需要有源位置和目标位置的绝对文件路径。
首先,使用我的 getPath()
方法或 uri.getPath()
:
获取绝对文件路径
String src = getPath(uri); /* Method defined above. */
或
Uri uri = data.getData();
String src = uri.getPath();
然后,创建两个 File
对象如下:
File source = new File(src);
String filename = uri.getLastPathSegment();
File destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CustomFolder/" + filename);
其中 CustomFolder
是您要将文件复制到的外部驱动器上的目录。
然后使用以下方法将一个文件从一个地方复制到另一个地方:
private void copy(File source, File destination) {
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();
try {
in.transferTo(0, in.size(), out);
} catch(Exception){
// post to log
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
试试这个。这应该有效。
注意: Vis-a-vis Lukas 的回答 - 他所做的是使用一种名为 openInputStream()
的方法,该方法 returns content of a Uri
,无论 Uri
表示文件还是 URL.
另一种有前途的方法 - FileProvider
:
还有一种方法可以从另一个应用程序获取文件。如果应用程序通过 FileProvider
, then it is possible to get hold of a FileDescriptor
对象共享其文件,该对象包含有关此文件的特定信息。
为此,请使用以下 Intent
:
Intent mRequestFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
mRequestFileIntent.setType("*/*");
startActivityForResult(mRequestFileIntent, 0);
在你的 onActivityResult()
:
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent returnIntent) {
// If the selection didn't work
if (resultCode != RESULT_OK) {
// Exit without doing anything else
return;
} else {
// Get the file's content URI from the incoming Intent
Uri returnUri = returnIntent.getData();
/*
* Try to open the file for "read" access using the
* returned URI. If the file isn't found, write to the
* error log and return.
*/
try {
/*
* Get the content resolver instance for this context, and use it
* to get a ParcelFileDescriptor for the file.
*/
mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e("MainActivity", "File not found.");
return;
}
// Get a regular file descriptor for the file
FileDescriptor fd = mInputPFD.getFileDescriptor();
...
}
}
其中 mInputPFD
是 ParcelFileDescriptor
。
参考文献:
1. Common Intents - File Storage.
2. FileChannel
.
3. FileProvider
.
在该方法中传递onActivityResult中返回的URI
private String getPath(Uri contentURI) {
String result;
Cursor cursor = getActivity().getContentResolver().query(contentURI,
null, null, null, null);
if (cursor == null) {
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor
.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
我做了同样的事情,让用户从文件夹中选择一张图片:
1) 有一个按钮OPEN:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_open:
myOpenImagePicker();
break;
}
}
2) 打开图片文件夹功能:
@SuppressLint("InlinedApi")
public void myOpenImagePicker() {
if (Build.VERSION.SDK_INT < 19) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
SELECT_FOLDER);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, SELECT_FOLDER);
}
}
3) activity 结果,我获取图像文件路径并使用图像路径执行任何我想做的事情:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case SELECT_FOLDER:
if (resultCode == RESULT_OK && data != null) {
Uri originalUri = data.getData();
String id01 = W_ImgFilePathUtil.getPath(
getApplicationContext(), originalUri);
Bitmap unscaledBitmap = W_ImgScalingUtil.decodeResource(id01,
xdrawing.getViewWidth(), xdrawing.getViewHeight(),
ScalingLogic.FIT);
if (unscaledBitmap == null) {
zprefsutil.ShowToast("IMAGE ERROR", 1);
} else {
setExternalScaledBitmap(W_ImgScalingUtil
.createScaledBitmap(unscaledBitmap,
xdrawing.getViewWidth(),
xdrawing.getViewHeight(), ScalingLogic.FIT));
unscaledBitmap.recycle();
xdrawing.invalidate();
}
}
break;
default:
break;
}
}
4) 现在是最重要的部分,W_ImgFilePathUtil class,代码不是我提供的,但它允许您检索任何选定文件的完整路径无论是在 SD 卡上,google 驱动器,...:[=34=]
public class W_ImgFilePathUtil {
/**
* Method for return file path of Gallery image
*
* @param context
* @param uri
* @return path of the selected image file from gallery
*/
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
// check here to KITKAT or new version
final boolean isKitKatorUp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKatorUp && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/"
+ split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] { split[1] };
return getDataColumn(context, contentUri, selection,
selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context
* The context.
* @param uri
* The Uri to query.
* @param selection
* (Optional) Filter used in the query.
* @param selectionArgs
* (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri
.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri
.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri
.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri
.getAuthority());
}
}
结论:代码适用于图像路径,但肯定适用于任何类型的文件。
希望这有助于解决您的问题。
和平。
以下是如何实现文件选择器并将所选文件移动到另一个位置(例如图片)。
首先,在您的代码中添加一个带有点击监听器按钮的文件选择器,如下所示:
一个选择文件的按钮:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_choose_file:
showFileChooser();
break;
}
}
private String filePath = null;
private File sourceFile;
private static final int FILE_SELECT_CODE = 0;
private void showFileChooser() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Copy"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
}
然后像这样处理 onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FILE_SELECT_CODE:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
Uri uri = data.getData();
File file = new File(getCacheDir(), getFileName(uri));
int maxBufferSize = 1 * 1024 * 1024;
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
Log.e("InputStream Size","Size " + inputStream);
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e("File Size","Size " + file.length());
inputStream.close();
outputStream.close();
file.getPath();
Log.e("File Path","Path " + file.getPath());
file.length();
Log.e("File Size","Size " + file.length());
if(file.length() > 0){
sourceFile = file;
saveFile(sourceFile);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
} else {
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
private void saveFile (File sourceFile) {
try {
File currentFile = sourceFile;
Bitmap myBitmap = BitmapFactory.decodeFile(currentFile.getAbsolutePath());
String path = currentFile.getAbsolutePath();
String idStr = path.substring(path.lastIndexOf('/') + 1);
File filepath = Environment.getExternalStorageDirectory();
File dir = new File(filepath.getAbsolutePath() + "/" + "yourFolderName" + "/");
if (!dir.exists()) {
dir.mkdirs();
}
String fileName = currentFile.getName();
file = new File(dir, fileName);
if (file.exists()) file.delete();
FileOutputStream fos = new FileOutputStream(file);
myBitmap.compress(CompressFormat.JPEG, 65, fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
注意:不要忘记将此权限添加到清单文件。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
希望这对您有所帮助。
使用 ActivityResultLauncher
它的工作原理如下:
ActivityResultLauncher<Intent> startActivityForResult = requireActivity().registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
Uri contentUri = data.getData();
...
}
});
用法示例:
Intent data = new Intent(Intent.ACTION_GET_CONTENT);
data.addCategory(Intent.CATEGORY_OPENABLE);
data.setType("*/*");
Intent intent = Intent.createChooser(data, "Choose a file");
startActivityForResult.launch(intent);
需要以下依赖项(有或没有-ktx
):
implementation "androidx.activity:activity:1.2.3"
我正在尝试在我的 Android 项目中实现文件选择器。到目前为止我能做的是:
Intent chooseFile;
Intent intent;
chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
intent = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(intent, PICKFILE_RESULT_CODE);
然后在我的 onActivityResult()
switch(requestCode){
case PICKFILE_RESULT_CODE:
if(resultCode==-1){
Uri uri = data.getData();
String filePath = uri.getPath();
Toast.makeText(getActivity(), filePath,
Toast.LENGTH_LONG).show();
}
break;
}
这是打开文件选择器,但这不是我想要的。例如,我想select一个文件(.txt),然后得到那个File
然后使用它。使用这段代码,我以为我会得到 完整路径 但它没有发生;例如我得到:/document/5318/
。但是通过这条路径我无法获取文件。我创建了一个名为 PathToFile()
的方法 returns a File
:
private File PathToFile(String path) {
File tempFileToUpload;
tempFileToUpload = new File(path);
return tempFileToUpload;
}
我想做的是让用户从任何地方选择 File
表示 DropBox
、Drive
、SDCard
、Mega
、等等...我找不到正确的方法,我试图获得 Path
然后通过此 Path
获得 File
... 但它没有工作,所以我认为最好获取 File
本身,然后以编程方式使用此 File
我 Copy
此或 Delete
.
编辑(当前代码)
我的Intent
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);
我有一个问题,因为我不知道 text/plain
支持什么,但我会调查一下,但目前这并不重要。
在我的 onActivityResult()
上,我使用了与 Copy
这个 File
到另一个部分来自我的 SDcard
我正在等待他的回答。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
//get the path
Log.d("Path???", content_describer.getPath());
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getActivity().getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
text.setText(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
getPath()
来自@Y.S。
我正在这样做:
String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getActivity().getContentResolver().query(content_describer, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
cursor.close();
Log.d( "PATH-->",cursor.getString(column_index));
得到 NullPointerException
:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=131073, result=-1, data=Intent { dat=file:///path typ=text/plain flg=0x3 }} to activity {info.androidhive.tabsswipe/info.androidhive.tabsswipe.MainActivity2}: java.lang.NullPointerException
使用代码进行编辑感谢 @Y.S., @Lukas Knuth, and @CommonsWare。
这是Intent
,我只接受文件text/plain
。
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);
在我的 onActivityResult()
上,我创建了一个 URI
来获取 Intent
的数据,我创建了一个 File
来保存 绝对路径 做 content_describer.getPath();
,然后我保留路径名称以在 TextView
和 content_describer.getLastPathSegment();
中使用它(那太棒了 @Y.S。不知道那个函数),我创建了第二个 File
,我称之为 destination
,然后我发送 AbsolutePath
来创建这个 File
.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
String src = content_describer.getPath();
source = new File(src);
Log.d("src is ", source.toString());
String filename = content_describer.getLastPathSegment();
text.setText(filename);
Log.d("FileName is ",filename);
destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Test/TestTest/" + filename);
Log.d("Destination is ", destination.toString());
SetToFolder.setEnabled(true);
}
}
我还创建了一个函数,您必须发送我们之前创建的 source file
和 destination file
以将其复制到新文件夹。
private void copy(File source, File destination) throws IOException {
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();
try {
in.transferTo(0, in.size(), out);
} catch(Exception e){
Log.d("Exception", e.toString());
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
我还创建了一个函数,告诉我这个文件夹是否存在(我必须发送 destination file
,如果它不存在,我创建这个文件夹,如果不存在,我不要什么都不做。
private void DirectoryExist (File destination) {
if(!destination.isDirectory()) {
if(destination.mkdirs()){
Log.d("Carpeta creada","....");
}else{
Log.d("Carpeta no creada","....");
}
}
再次感谢您的帮助,希望您喜欢这段与大家一起制作的代码:)
A Uri
is not a file。 Uri
更接近 Web 服务器 URL。这是一个不透明的地址,仅对 "server"(或在本例中为 ContentProvider
)有意义。
正如您使用 InputStream
读取由 Web URL 表示的字节一样,您使用 InputStream
读取由 [=10= 表示的字节].您可以通过在 ContentResolver
.
openInputStream()
来获得这样的流
正如 @CommonsWare 已经指出的,Android returns 你 Uri
,这是一个比文件路径更抽象的概念.
它也可以描述一个简单的文件路径,但它也可以描述通过通过一个应用程序(如content://media/external/audio/media/710
)访问的资源。
如果您希望您的用户从 phone 中选择任何文件以从您的应用程序中读取它,您可以通过请求文件(正如您所做的那样)然后使用 ContentResolver
以获得选择器返回的 Uri
的 InputStream
。
这是一个例子:
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
// Ask specifically for something that can be opened:
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("*/*");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_REQUEST_CODE
);
// And then somewhere, in your activity:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_REQUEST_CODE && resultCode == RESULT_OK){
Uri content_describer = data.getData();
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
some_view.setText(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
重要提示:一些提供商(如 Dropbox)store/cache 将他们的数据存储在外部存储中。您需要在清单中声明 android.permission.READ_EXTERNAL_STORAGE
-permission,否则您将获得 FileNotFoundException
,即使文件在那里。
更新:是的,您可以通过从一个流读取文件并将其写入另一个流来复制文件:
// Error handling is omitted for shorter code!
Uri content_describer = data.getData();
InputStream in = null;
OutputStream out = null;
try {
// open the user-picked file for reading:
in = getContentResolver().openInputStream(content_describer);
// open the output-file:
out = new FileOutputStream(new File("some/path/to/a/writable/file"));
// copy the content:
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
// Contents are copied!
} finally {
if (in != null) {
in.close();
}
if (out != null){
out.close();
}
}
可能无法删除该文件,因为该文件不属于您,它属于与您共享它的应用程序。因此,所属应用程序负责删除该文件。
第 1 步 - 使用隐式 Intent
:
要从设备中选择文件,您应该使用隐式 Intent
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(chooseFile, PICKFILE_RESULT_CODE);
第 2 步 - 获取绝对文件路径:
要从 Uri
获取文件路径,请首先尝试使用
Uri uri = data.getData();
String src = uri.getPath();
其中 data
是 onActivityResult()
中返回的 Intent
。
如果这不起作用,请使用以下方法:
public String getPath(Uri uri) {
String path = null;
String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if(cursor == null){
path = uri.getPath()
}
else{
cursor.moveToFirst();
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
path = cursor.getString(column_index);
cursor.close();
}
return ((path == null || path.isEmpty()) ? (uri.getPath()) : path);
}
这两种方法中至少有一种应该可以为您提供正确的完整路径。
第 3 步 - 复制文件:
我相信您想要的是将文件从一个位置复制到另一个位置。
为此,需要有源位置和目标位置的绝对文件路径。
首先,使用我的 getPath()
方法或 uri.getPath()
:
String src = getPath(uri); /* Method defined above. */
或
Uri uri = data.getData();
String src = uri.getPath();
然后,创建两个 File
对象如下:
File source = new File(src);
String filename = uri.getLastPathSegment();
File destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CustomFolder/" + filename);
其中 CustomFolder
是您要将文件复制到的外部驱动器上的目录。
然后使用以下方法将一个文件从一个地方复制到另一个地方:
private void copy(File source, File destination) {
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();
try {
in.transferTo(0, in.size(), out);
} catch(Exception){
// post to log
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
试试这个。这应该有效。
注意: Vis-a-vis Lukas 的回答 - 他所做的是使用一种名为 openInputStream()
的方法,该方法 returns content of a Uri
,无论 Uri
表示文件还是 URL.
另一种有前途的方法 - FileProvider
:
还有一种方法可以从另一个应用程序获取文件。如果应用程序通过 FileProvider
, then it is possible to get hold of a FileDescriptor
对象共享其文件,该对象包含有关此文件的特定信息。
为此,请使用以下 Intent
:
Intent mRequestFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
mRequestFileIntent.setType("*/*");
startActivityForResult(mRequestFileIntent, 0);
在你的 onActivityResult()
:
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent returnIntent) {
// If the selection didn't work
if (resultCode != RESULT_OK) {
// Exit without doing anything else
return;
} else {
// Get the file's content URI from the incoming Intent
Uri returnUri = returnIntent.getData();
/*
* Try to open the file for "read" access using the
* returned URI. If the file isn't found, write to the
* error log and return.
*/
try {
/*
* Get the content resolver instance for this context, and use it
* to get a ParcelFileDescriptor for the file.
*/
mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e("MainActivity", "File not found.");
return;
}
// Get a regular file descriptor for the file
FileDescriptor fd = mInputPFD.getFileDescriptor();
...
}
}
其中 mInputPFD
是 ParcelFileDescriptor
。
参考文献:
1. Common Intents - File Storage.
2. FileChannel
.
3. FileProvider
.
在该方法中传递onActivityResult中返回的URI
private String getPath(Uri contentURI) {
String result;
Cursor cursor = getActivity().getContentResolver().query(contentURI,
null, null, null, null);
if (cursor == null) {
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor
.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
我做了同样的事情,让用户从文件夹中选择一张图片:
1) 有一个按钮OPEN:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_open:
myOpenImagePicker();
break;
}
}
2) 打开图片文件夹功能:
@SuppressLint("InlinedApi")
public void myOpenImagePicker() {
if (Build.VERSION.SDK_INT < 19) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
SELECT_FOLDER);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, SELECT_FOLDER);
}
}
3) activity 结果,我获取图像文件路径并使用图像路径执行任何我想做的事情:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case SELECT_FOLDER:
if (resultCode == RESULT_OK && data != null) {
Uri originalUri = data.getData();
String id01 = W_ImgFilePathUtil.getPath(
getApplicationContext(), originalUri);
Bitmap unscaledBitmap = W_ImgScalingUtil.decodeResource(id01,
xdrawing.getViewWidth(), xdrawing.getViewHeight(),
ScalingLogic.FIT);
if (unscaledBitmap == null) {
zprefsutil.ShowToast("IMAGE ERROR", 1);
} else {
setExternalScaledBitmap(W_ImgScalingUtil
.createScaledBitmap(unscaledBitmap,
xdrawing.getViewWidth(),
xdrawing.getViewHeight(), ScalingLogic.FIT));
unscaledBitmap.recycle();
xdrawing.invalidate();
}
}
break;
default:
break;
}
}
4) 现在是最重要的部分,W_ImgFilePathUtil class,代码不是我提供的,但它允许您检索任何选定文件的完整路径无论是在 SD 卡上,google 驱动器,...:[=34=]
public class W_ImgFilePathUtil {
/**
* Method for return file path of Gallery image
*
* @param context
* @param uri
* @return path of the selected image file from gallery
*/
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
// check here to KITKAT or new version
final boolean isKitKatorUp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKatorUp && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/"
+ split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] { split[1] };
return getDataColumn(context, contentUri, selection,
selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context
* The context.
* @param uri
* The Uri to query.
* @param selection
* (Optional) Filter used in the query.
* @param selectionArgs
* (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri
.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri
.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri
.getAuthority());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri
.getAuthority());
}
}
结论:代码适用于图像路径,但肯定适用于任何类型的文件。
希望这有助于解决您的问题。
和平。
以下是如何实现文件选择器并将所选文件移动到另一个位置(例如图片)。
首先,在您的代码中添加一个带有点击监听器按钮的文件选择器,如下所示:
一个选择文件的按钮:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_choose_file:
showFileChooser();
break;
}
}
private String filePath = null;
private File sourceFile;
private static final int FILE_SELECT_CODE = 0;
private void showFileChooser() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Copy"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
}
然后像这样处理 onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FILE_SELECT_CODE:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
Uri uri = data.getData();
File file = new File(getCacheDir(), getFileName(uri));
int maxBufferSize = 1 * 1024 * 1024;
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
Log.e("InputStream Size","Size " + inputStream);
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e("File Size","Size " + file.length());
inputStream.close();
outputStream.close();
file.getPath();
Log.e("File Path","Path " + file.getPath());
file.length();
Log.e("File Size","Size " + file.length());
if(file.length() > 0){
sourceFile = file;
saveFile(sourceFile);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
} else {
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
private void saveFile (File sourceFile) {
try {
File currentFile = sourceFile;
Bitmap myBitmap = BitmapFactory.decodeFile(currentFile.getAbsolutePath());
String path = currentFile.getAbsolutePath();
String idStr = path.substring(path.lastIndexOf('/') + 1);
File filepath = Environment.getExternalStorageDirectory();
File dir = new File(filepath.getAbsolutePath() + "/" + "yourFolderName" + "/");
if (!dir.exists()) {
dir.mkdirs();
}
String fileName = currentFile.getName();
file = new File(dir, fileName);
if (file.exists()) file.delete();
FileOutputStream fos = new FileOutputStream(file);
myBitmap.compress(CompressFormat.JPEG, 65, fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
注意:不要忘记将此权限添加到清单文件。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
希望这对您有所帮助。
使用 ActivityResultLauncher
它的工作原理如下:
ActivityResultLauncher<Intent> startActivityForResult = requireActivity().registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
Uri contentUri = data.getData();
...
}
});
用法示例:
Intent data = new Intent(Intent.ACTION_GET_CONTENT);
data.addCategory(Intent.CATEGORY_OPENABLE);
data.setType("*/*");
Intent intent = Intent.createChooser(data, "Choose a file");
startActivityForResult.launch(intent);
需要以下依赖项(有或没有-ktx
):
implementation "androidx.activity:activity:1.2.3"