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

49

我正在尝试在我的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),然后获取那个 文件 并使用它。通过这段代码,我以为会得到完整的路径,但事实并非如此;例如,我得到了:/document/5318/。但是,使用这个路径我无法获取文件。 我创建了一个名为 PathToFile() 的方法,返回了一个 文件

 private File PathToFile(String path) {
    File tempFileToUpload;
    tempFileToUpload = new File(path);
    return tempFileToUpload;
}
我试图让用户从任何地方选择一个“文件”(包括DropBox、Drive、SDCard、Mega等),但是我找不到正确的方法。我尝试获取“路径”,然后通过这个“路径”获取一个“文件”...但是它不起作用,所以我认为更好的方法是直接获取“文件”,然后通过编程方式对此“文件”进行“复制”或“删除”。

编辑(当前代码)

我的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的回答相同的方法,但我不知道是否可以使用它将此文件复制到我的SD卡的另一个位置,我正在等待他的答案。

@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));
得到了一个空指针异常:

java.lang.RuntimeException:将结果ResultInfo {who = null,request = 131073,result = -1,data = Intent {dat = file:/// path typ = text / plain flg = 0x3}}传递给活动 {info.androidhive.tabsswipe / info.androidhive.tabsswipe .MainActivity2}:java.lang.NullPointerException

编辑带有代码的工作感谢@Y.S。@Lukas Knuth@CommonsWare

这是我只接受文件text/plain类型的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
);
在我的 onActivityResult() 中,我创建了一个 URI ,从中获取了 Intent 的数据,然后通过 content_describer.getPath(); 创建了一个 File 来保存绝对路径,并将路径的名称保留下来以便在 TextView 中使用 content_describer.getLastPathSegment();(这个函数真是太棒了,@Y.S.让我第一次知道它的存在),然后我创建了第二个名为 destinationFile ,并将 AbsolutePath 发送给它来创建这个文件。
@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);
    }
}

我还创建了一个函数,你需要向其发送之前创建的源文件目标文件,以将其复制到新文件夹中。

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

我也创建了一个函数,用于判断这个文件夹是否存在(我必须发送 目标文件 ,如果不存在,我就创建这个文件夹;如果存在,则不进行任何操作)。

private void DirectoryExist (File destination) {

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

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

8个回答

77

步骤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();
   }
}

试试这个。这应该可以工作。

注意: 关于Lukas的答案 - 他所做的是使用了一个叫做openInputStream()的方法,该方法返回Uri内容,无论该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();
        ...
    }
}

其中,mInputPFD 是一个 ParcelFileDescriptor

参考资料:

1. 常用 Intent - 文件存储.

2. FileChannel

3. FileProvider

4. 请求共享文件


评论不适合进行长时间的讨论;此对话已被移至聊天室 - Martijn Pieters
@tohidmahmoudvand:对我和其他人都有效,请创建一个新问题并提供详细信息,说明具体哪些方面出现了问题。 - Yash Sampat

12

正如@CommonsWare所指出的那样,Android会返回一个Uri,这是一个比文件路径更抽象的概念。

它也可以描述一个简单的文件路径,但也可以描述通过应用程序访问的资源(例如content://media/external/audio/media/710)。

如果你想让用户从手机中选择任何文件来从你的应用程序中读取它,你可以通过请求文件(就像你正确地做的那样),然后使用ContentResolver获取由选择器返回的UriInputStream

以下是一个示例:

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)会将其数据存储/缓存到外部存储器上。你需要在你的清单文件中声明 android.permission.READ_EXTERNAL_STORAGE 权限,否则即使文件存在,你也会遇到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();
    }
}

由于文件不是属于您,而是属于与您共享它的应用程序,因此可能无法删除该文件。因此,拥有该文件的应用程序负责删除该文件。


谢谢,但我可以使用这个文件吗?也就是说,我可以将其复制到另一个文件夹中,或者只是删除它等等?我可以对这个文件做些什么吗? - Skizo-ozᴉʞS ツ
我更新了我的答案,加上了复制代码和一些关于删除文件的解释。如果这还不够,请告诉我您想要实现什么目标。 - Lukas Knuth
我尝试了你的代码,但是我必须使用你的第一个代码将其写入字符串,然后使用新的代码进行编写,对吗?你知道为什么我在SD卡上创建了一个新文件,但在我的电脑上看不到它吗?这意味着我只能通过我的设备上的文件浏览器来访问它。 - Skizo-ozᴉʞS ツ
如何确定远程图片的文件类型或扩展名,例如如果从Google Drive上选择了一张图片。 - Raghav Satyadev
我找到了一种更好的方法来解决这个问题,有时候getType方法会返回null。请查看我在@LukasKnuth发布的答案。 https://dev59.com/E3A65IYBdhLWcg3w5zHB#48257741 - Raghav Satyadev
显示剩余4条评论

9
使用 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.5.0"

7

我做了同样的事情,让用户从文件夹中选择图像:

1)有一个"打开"按钮:

@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) 活动结果,其中我可以获取图像文件路径并对其进行任何处理:

@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类,这段代码不是我写的,但它允许您检索任何选择文件的完整路径,无论其是在SD卡、谷歌云端硬盘等位置:

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

结论:该代码适用于图像路径,但肯定也适用于任何类型的文件。
希望这能帮助解决你的问题。
祝一切平安。

6
Uri 并不是文件。 Uri 更接近于 Web 服务器 URL。它是一个不透明的地址,只有对「服务器」(或在这种情况下是ContentProvider)有意义。

就像你使用 InputStream 来读取由 Web URL 表示的字节一样,你也可以使用 InputStream 来读取由Uri 表示的字节。通过调用 ContentResolver 上的 openInputStream(),您可以获得这样的流。


我也使用了 String FilePath = data.getData().getPath(); 但是它没有给我正确的路径...你能给我一个示例来解释一下吗?你明白我要做什么吗? - Skizo-ozᴉʞS ツ

3

以下是如何实现文件选择器并将所选文件移动到另一个位置(例如图片)的方法。

首先,在您的代码中添加一个带有按钮点击监听器的文件选择器,如下所示:

选择文件的按钮:

@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" />

希望这能帮到你。

2
在这个方法中传递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;
}

抛出异常:IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it. 在这一行 result = cursor.getString(idx) - Raj Narayanan

2
这是在较新版本的安卓系统中如何操作:
private ActivityResultLauncher<String> filePicker;

//execute this in your AppCompatActivity onCreate
public void registerFilePicker() {
    filePicker = registerForActivityResult(
            new ActivityResultContracts.GetContent(), 
            uri -> onPickFile(uri)
    );
}

//execute this to launch the picker
public void openFilePicker() {
    String mimeType = "*/*";
    filePicker.launch(mimeType);
}

//this gets executed when the user picks a file
public void onPickFile(Uri uri) {
    try (InputStream inputStream = getContentResolver().openInputStream(uri)) {

    } catch (IOException exception) {

    }
}

更多信息:

https://developer.android.com/training/basics/intents/result


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接