【问题标题】:Creating file from Uri从 Uri 创建文件
【发布时间】:2018-01-13 04:57:43
【问题描述】:

我正在尝试将图像上传到 php,为此我需要将文件发送到服务器。所以我正在尝试从 data 参数创建一个文件。

但是我收到了这个错误Cannot resolve constructor File

这是我的代码:

 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
            // Get the url from data
            Uri selectedImageUri = data.getData();
            if (null != selectedImageUri) {
                // File
                File imageFilePath = new File(selectedImageUri);

【问题讨论】:

  • 在if条件下你必须检查if语句或switch中的requestCode
  • 你能告诉我一个 data.getData() 值吗?
  • 在哪一行出现异常?
  • 你使用哪个网络库?

标签: android


【解决方案1】:

适当的方法是 getContentResolver().openInputStream(uri_of_your_file); 并使用 FileOutputStream 到您想要的路径,然后使用该文件。

来自Commonsware answer

您可以使用 ContentResolver 和 openInputStream() 来获取 InputStream 上由 Uri 表示的内容。您可以创建一个 FileOutputStream 在您控制的某个文件上。而且,您可以使用 Java 从 InputStream 复制到 OutputStream 的 I/O,制作您自己的 复制您控制的文件中的内容。

执行此操作的示例代码,

InputStream in =  getContentResolver().openInputStream("your_uri_here");
OutputStream out = new FileOutputStream(new File("your_file_here"));
        byte[] buf = new byte[1024];
        int len;
        while((len=in.read(buf))>0){
            out.write(buf,0,len);
        }
        out.close();
        in.close();

【讨论】:

  • 哪个文件进入“your_file_here”???因为我正在尝试创建文件,但我没有任何文件..
  • 用路径创建一个文件。
  • 这是 100% 正确的,但仍然很糟糕。想象一下,必须复制/创建一个 2GB 的文件,用户将不得不等待一段时间才能完成文件。我知道我可以使用 Uri 打开一个流,然后使用该流,但是一些 api/库要求我们传递一个字符串路径。我希望 Google 能够理解这一点并为我们提供不同的方法。
  • 另外,更深入的解释,请看下面我的回答。
  • @HB。此代码可以在任何地方使用,具体取决于要执行的组件,它可以是任何东西、活动或片段或任何其他适当的地方。
【解决方案2】:

@GeekDroid's 的答案是正确的,但我想我会提供更多信息来帮助澄清。

I've also created a library that will return the file path from an Uri when selecting a File from MediaStore.


我创建了一个类,它将在您的应用程序目录中复制/创建一个新文件。这是在后台线程上完成的,应该这样做。

我还创建了一个回调接口来获取“状态”并显示ProgressBar

这是接口(你可以改变名字和回调):

interface CallBackTask {
    void onCopyPreExecute();
    void onCopyProgressUpdate(int progress);
    void onCopyPostExecute(String path, boolean wasSuccessful, String reason);
}

这里是复制文件的类:

public class CopyFileAsyncTask extends AsyncTask<Uri, Integer, String> {
    private Uri mUri;
    private CallBackTask callback;
    private WeakReference<Context> mContext;
    private String pathPlusName;
    private File folder;
    private Cursor returnCursor;
    private InputStream is = null;
    private String errorReason = "";

    DownloadAsyncTask(Uri uri, Context context, CallBackTask callback) {
        this.mUri = uri;
        mContext = new WeakReference<>(context);
        this.callback = callback;
    }

    @Override
    protected void onPreExecute() {
        callback.onCopyPreExecute();
        Context context = mContext.get();
        if (context != null) {
            folder = context.getExternalFilesDir("Temp");
            returnCursor = context.getContentResolver().query(mUri, null, null, null, null);
            try {
                is = context.getContentResolver().openInputStream(mUri);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        int post = values[0];
        callback.onCopyProgressUpdate(post);
    }

    @Override
    protected String doInBackground(Uri... params) {
        File file = null;
        int size = -1;

        try {
            try {
                if (returnCursor != null && returnCursor.moveToFirst()){
                    if (mUri.getScheme() != null)
                    if (mUri.getScheme().equals("content")) {
                        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                        size = (int) returnCursor.getLong(sizeIndex);
                    }else if (mUri.getScheme().equals("file")) {
                        File ff = new File(mUri.getPath());
                        size = (int) ff.length();
                    }
                }
            }
            finally {
                if (returnCursor != null)
                returnCursor.close();
            }

            pathPlusName = folder + "/" + getFileName(mUri, mContext.get());
            file = new File(folder + "/" + getFileName(mUri, mContext.get()));

            BufferedInputStream bis = new BufferedInputStream(is);
            FileOutputStream fos = new FileOutputStream(file);


            byte[] data = new byte[1024];
            long total = 0;
            int count;
            while ((count = bis.read(data)) != -1) {
                if (!isCancelled()) {
                    total += count;
                    if (size != -1) {
                        publishProgress((int) ((total * 100) / size));
                    }
                    fos.write(data, 0, count);
                }
            }
            fos.flush();
            fos.close();

        } catch (IOException e) {
            errorReason = e.getMessage();
        }

        return file.getAbsolutePath();

    }

    private String getFileName(Uri uri, Context context) {
        String result = null;
        if (uri.getScheme() != null) {
            if (uri.getScheme().equals("content")) {
                Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
                if (cursor != null && cursor.moveToFirst()) {
                    result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                }
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        if (result == null) {
            result = uri.getPath();
            assert result != null;
            int cut = result.lastIndexOf('/');
            if (cut != -1) {
                result = result.substring(cut + 1);
            }
        }
        return result;
    }

    protected void onPostExecute(String result) {
        if(result == null){
            callback.onCopyPostExecute(pathPlusName, false, errorReason);
        }else {
            callback.onCopyPostExecute(pathPlusName, true, "");
        }
    }
}

在您的Activity 中,您应该执行以下操作:

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class ExampleActivity extends AppCompatActivity implements CallBackTask{
    Button someBtn;
    Uri someUri = .... //This is just an example, you should provide your own Uri

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        someBtn = findViewById(R.id.someBtn);

        someBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CopyFileAsyncTask asyntask = new CopyFileAsyncTask(someUri, ExampleActivity.this, ExampleActivity.this);
                asyntask.execute();
            }
        });

    }
    
    ProgressBar mProgressBar;
    TextView percentText;
    private AlertDialog mdialog;

    @Override
    public void onCopyPreExecute() {
        final AlertDialog.Builder mPro = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.myDialog));
        @SuppressLint("InflateParams") final View mPView = LayoutInflater.from(this).inflate(R.layout.dailog_layout, null);
        percentText = mPView.findViewById(R.id.percentText);

        mProgressBar = mPView.findViewById(R.id.mProgressBar);
        mProgressBar.setMax(100);
        mPro.setView(mPView);
        mdialog = mPro.create();
        mdialog.show();
    }

    @Override
    public void onCopyProgressUpdate(int progress) {
        String progressPlusPercent = progress + "%";
        percentText.setText(progressPlusPercent);
        mProgressBar.setProgress(progress);
    }

    @Override
    public void onCopyPostExecute(String path, boolean wasSuccessful, String reason) {
        if (mdialog != null && mdialog.isShowing()) {
            mdialog.cancel();
        }

        if (wasSuccessful){
            Toast.makeText(this, "File was created at - "+path, Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(this, "Error - "+reason, Toast.LENGTH_SHORT).show();
        }
        
    }
}

您可以随时取消复制文件,方法是调用:

if (asyntask!=null){
    asyntask.cancel(true);
}

如果您想完全按照我的方式实现它并在复制文件时显示ProgressBar,那么这里是我的对话框的布局和样式:

R.style.myDialog:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="myDialog" parent="Theme.AppCompat.Dialog.Alert">
        <item name="android:windowNoTitle">true</item>
        <item name="android:background">#fff</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>

</resources>

R.layout.dailog_layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <RelativeLayout
        android:id="@+id/loadContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:padding="20dp">

        <RelativeLayout
            android:id="@+id/dotRel"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/percentText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginBottom="15dp"
                android:text="0 %"
                android:textColor="@android:color/black"
                android:textSize="25sp" />

            <ProgressBar
                android:id="@+id/mProgressBar"
                style="@android:style/Widget.ProgressBar.Horizontal"
                android:layout_width="match_parent"
                android:layout_height="5dp"
                android:layout_below="@+id/percentText"
                android:progressDrawable="@drawable/progressbar" />

        </RelativeLayout>

    </RelativeLayout>

</RelativeLayout>

这是复制文件时的对话框:


结论:

通过上述方式,您的文件将被复制到/storage/emulated/0/Android/data/yourPackageName/files/Temp/YourFile.jpg。在复制文件时,将显示一个进度对话框,以百分比表示进度(这对于复制大文件很有用)。如果复制文件时出错,原因将在onCopyPostExecute中提供

这将适用于文件和内容 Uri。

【讨论】:

  • 谢谢!这是读取大文件时使用的最佳方法...但问题是手机存储空间有限,因此直接读取文件比复制文件要好得多...
  • @Skaldebane 这个问题询问如何从 Uri 创建文件。我创建了一个从 Uri 返回文件路径的库,然后您不必复制文件,除非它在 ​​Google Drive 上 - github.com/HBiSoft/PickiT
【解决方案3】:

一站式解决您的问题

File imageFile = new File(selectedImageUri.getPath());

你错过了

.getPath()

【讨论】:

  • 在 Android 10 中会失败
  • @Louis Nguyen 有什么解决方案吗?在 Android 10 中找不到文件
  • @Ram 你应该为这个文件创建一个 io 流并将它保存到一个临时文件中。使用这个临时文件做一些事情,完成后删除它!
  • 这只会切断资源定位器中的content://com.android.externalstorage.documents 部分。这不会为您提供任何类型的文件 I/O 的可用路径。
【解决方案4】:

您可以使用此功能从新 android 和旧版本的 uri 中获取文件:

fun getFileFromUri(uri: Uri): File? {
    if (uri.path == null) {
        return null
    }
    var realPath = String()
    val databaseUri: Uri
    val selection: String?
    val selectionArgs: Array<String>?
    if (uri.path!!.contains("/document/image:")) {
        databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        selection = "_id=?"
        selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
    } else {
        databaseUri = uri
        selection = null
        selectionArgs = null
    }
    try {
        val column = "_data"
        val projection = arrayOf(column)
        val cursor = context.contentResolver.query(
            databaseUri,
            projection,
            selection,
            selectionArgs,
            null
        )
        cursor?.let {
            if (it.moveToFirst()) {
                val columnIndex = cursor.getColumnIndexOrThrow(column)
                realPath = cursor.getString(columnIndex)
            }
            cursor.close()
        }
    } catch (e: Exception) {
        Log.i("GetFileUri Exception:", e.message ?: "")
    }
    val path = if (realPath.isNotEmpty()) realPath else {
        when {
            uri.path!!.contains("/document/raw:") -> uri.path!!.replace(
                "/document/raw:",
                ""
            )
            uri.path!!.contains("/document/primary:") -> uri.path!!.replace(
                "/document/primary:",
                "/storage/emulated/0/"
            )
            else -> return null
        }
    }
    return File(path)
}

【讨论】:

    【解决方案5】:

    你可以试试这个;

    try {
                    Uri uri = data.getData();
                    String selectedFilePath = FilePath.getPath(getActivity(), uri);
                    final File file = new File(selectedFilePath);
    
                    new UploadFileToServer().execute(file);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
    

    并像这样定义FilePath类;

    import android.content.ContentUris;
    import android.content.Context;
     import android.database.Cursor;
      import android.net.Uri;
    import android.os.Build;
    import android.os.Environment;
    import android.provider.DocumentsContract;
     import android.provider.MediaStore;
    
     public class FilePath {
    
    /**
     * Method for return file path of Gallery image/ Document / Video / Audio
     *
     * @param context
     * @param uri
     * @return path of the selected image file from gallery
     */
    public static String getPath(final Context context, final Uri uri) {
    
        // check here to KITKAT or new version
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    
        // DocumentProvider
        if (isKitKat && 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());
    }
    }
    

    【讨论】:

    • 这不是您应该从任何Uri 获取数据的方式 - 请改用ContentResolver API
    • 似乎不起作用。看起来只是一个临时解决方案
    • 这仅适用于共享文件但 uri 可能包含任何内容的情况,因此更好地读取输入流并从中创建文件!
    • 当从下载文件夹中选择文件时抛出异常
    猜你喜欢
    • 1970-01-01
    • 2021-12-30
    • 2017-02-15
    相关资源
    最近更新 更多