web-dev-qa-db-ja.com

Androidでファイルピッカーを実装し、選択したファイルを別の場所にコピーする

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/を取得します。しかし、このパスではファイルを取得できません。 Fileを返すPathToFile()というメソッドを作成しました:

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

私がやろうとしていることは、ユーザーがFileを選択できるようにすることですDropBoxDriveSDCardMegaなど...そして、私はそれを正しく行う方法を見つけられないので、Pathを取得しようとしましたこのFileによるPath...が機能しないため、File自体を取得し、プログラムでこのFileを使用して、プログラムで私はCopy thisまたは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 と同じものを使用しましたが、それでCopyFileから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:結果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 のおかげでコードを編集して編集.

これは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();を実行し、名前を保持します。 content_describer.getLastPathSegment();TextViewでそれを使用するパス(@YSはその関数を知らなかったのはすごかったです)、私は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 fileを送信する必要があります。存在しない場合はこのフォルダーを作成し、存在しない場合は何もしません。

private void DirectoryExist (File destination) {

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

ご協力いただきありがとうございます。皆さんと一緒に作成したこのコードをお楽しみください:)

16
Skizo-ozᴉʞS

ステップ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);
}
_

これらの2つの方法の少なくとも1つで、正しいフルパスを取得できます。

ステップ3-ファイルをコピー:

あなたが望むのは、ある場所から別の場所にファイルをコピーすることです。

これを行うには、ソースと宛先の両方の場所の絶対ファイルパスが必要です。

まず、getPath()メソッドまたはuri.getPath()のいずれかを使用して絶対ファイルパスを取得します。

_String src = getPath(uri);    /* Method defined above. */
_

または

_Uri uri = data.getData();
String src = uri.getPath();
_

次に、次のように2つの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に対する回答-彼がしたことは、 content を返すopenInputStream()というメソッドを使用することですUri(ファイルまたはURLを表すUri)。

別の有望なアプローチ-FileProvider

別のアプリからファイルを取得する方法はもう1つあります。アプリが 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

FileProvider

4。共有ファイルのリクエスト

35
Y.S

ユーザーがフォルダから画像を選択できるように同じことをしました:

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

)画像ファイルのパスを取得し、画像のパスで必要な操作を行うアクティビティの結果:

@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カード、Googleドライブなどに取得することができます... :

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

結論:コードは画像パスで動作しますが、あらゆる種類のファイルで動作します。

これが問題の解決に役立つことを願っています。

平和。

5
mourad

@ CommonsWare既に述べたように、AndroidはUriを返します。これはファイルパスよりも抽象的な概念です。

単純なファイルパスも記述できますが、アクセスされるリソースを記述することもできますthroughアプリケーション(content://media/external/audio/media/710など)。

ユーザーが電話からファイルを選択してアプリケーションから読み取るようにしたい場合は、ファイルを要求して(正しく行ったように)、ContentResolverを使用してInputStreamは、ピッカーによって返されるUriを表します。

次に例を示します。

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- permissionを宣言する必要があります。そうしないと、ファイルが存在していてもFileNotFoundExceptionを取得できます。


更新:はい、1つのストリームから読み取り、別のストリームに書き込むことでファイルをコピーできます。

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

ファイルは削除できない可能性があります。ファイルはbelongではないため、ファイルを共有したアプリケーションに属しているためです。そのため、所有アプリケーションはファイルを削除する責任があります。

4
Lukas Knuth

このメソッドで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;
}

A UriはファイルではありませんUriはWebサーバーのURLに近いです。これは不透明なアドレスであり、「サーバー」(またはこの場合はContentProvider)に対してのみ意味を持ちます。

InputStreamを使用してWeb URLで表されるバイトを読み込むように、InputStreamを使用してUriで表されるバイトを読み込みます。 ContentResolveropenInputStream()を呼び出すことにより、このようなストリームを取得します。

1
CommonsWare