android - 在 Android 中实现文件选择器并将所选文件复制到另一个位置

标签 android android-intent android-file android-fileprovider android-implicit-intent

我正在尝试在我的 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;
}

这是打开一个文件选择器,但它不是我想要的。例如,我想选择一个文件 (.txt),然后获取该 File 并使用它。有了这段代码,我想我会得到完整路径,但它没有发生;例如我得到:/document/5318/。但是使用此路径我无法获取文件。我创建了一个名为 PathToFile() 的方法,它返回一个 File :

 private File PathToFile(String path) {
    File tempFileToUpload;
    tempFileToUpload = new File(path);
    return tempFileToUpload;
}

我想做的是让用户从任何地方选择一个 File 意味着 DropBoxDriveSDCardMega 等...而且我找不到正确的方法,我试图获取 Path 然后获取 File 通过这个 Path... 但它不起作用,所以我认为最好获取 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() 上,我使用了与 @Lukas Knuth answer 相同的方法,但我不知道是否可以通过它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 , 和 @CommonsWare .

这是我只接受文件 text/plainIntent

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 我保存 absolute path 执行 content_describer.getPath();,然后我将路径的名称保留在 TextViewcontent_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 filedestination 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 文件,如果它不存在我创建这个文件夹,如果它存在不是我什么都不做。

private void DirectoryExist (File destination) {

    if(!destination.isDirectory()) {
        if(destination.mkdirs()){
            Log.d("Carpeta creada","....");
        }else{
            Log.d("Carpeta no creada","....");
        }
    }

再次感谢您的帮助,希望您喜欢与大家一起制作的这段代码 :)

最佳答案

第 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();

其中 dataonActivityResult() 中返回的 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() 的方法,该方法返回 contentUri,无论该 Uri 表示文件还是 URL。

另一种有前途的方法 - FileProvider:

还有另一种方法可以从另一个应用程序获取文件。如果应用程序通过 FileProvider 共享其文件,那么就有可能获得 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();
        ...
    }
}

其中 mInputPFDParcelFileDescriptor

引用资料:

1. Common Intents - File Storage .

2. FileChannel .

3. FileProvider .

4. Requesting a Shared File .

关于android - 在 Android 中实现文件选择器并将所选文件复制到另一个位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30789116/

相关文章:

java - 键盘隐藏时隐藏光标

java - 键盘打开时 WebView 调整屏幕大小

android - 为什么 Android (Jelly Bean) 忽略额外的 RecognizerIntent (Kõnele)?

Android - 将 URL 转换为文件

android - 如何在表格布局的底部显示按钮?

android - 将数据发布到 MVC Web Api 时 Volley 库无法工作

android - 使用 backstack 启动 Activity

java - 安卓 "java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object"

java - Android getCacheDir() 中的文件何时被删除?长期存储的更好选择是什么?

android - 将应用程序与位于内部存储目录而不是 Assets 目录中的文件 bundle 在一起?