简体   繁体   English

从 Uri 创建文件

[英]Creating file from Uri

I'm trying to upload a image to php, for that I need to send a File to the server.我正在尝试将图像上传到 php,为此我需要将文件发送到服务器。 So I am trying to create a file from the data parameter.所以我试图从数据参数创建一个文件。

But I got this error Cannot resolve constructor File但我收到此错误Cannot resolve constructor File

Here's my code:这是我的代码:

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

The appropriate way is to getContentResolver().openInputStream(uri_of_your_file);合适的方法是getContentResolver().openInputStream(uri_of_your_file); and using a FileOutputStream to your desired path and then use that file.并使用FileOutputStream到您想要的路径,然后使用该文件。

As from Commonsware answer来自Commonsware的回答

You can use ContentResolver and openInputStream() to get an InputStream on the content represented by the Uri.您可以使用 ContentResolver 和 openInputStream() 来获取 Uri 表示的内容的 InputStream。 You can create a FileOutputStream on some file that you control.您可以在您控制的某个文件上创建 FileOutputStream。 And, you can use Java I/O to copy from the InputStream to the OutputStream, making your own copy of the content in a file that you control.而且,您可以使用 Java I/O 从 InputStream 复制到 OutputStream,在您控制的文件中制作您自己的内容副本。

Sample code for doing that,这样做的示例代码,

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

@GeekDroid's answer is correct, but I thought I would provide more info to help clarify. @GeekDroid 的回答是正确的,但我想我会提供更多信息来帮助澄清。

I've also created a library that will return the file path from an Uri when selecting a File from MediaStore .我还创建了一个库,当从MediaStore选择File时,它将从Uri返回文件路径。


I've created a class that will copy/create a new file inside your application's directory.我创建了一个类,它将在您的应用程序目录中复制/创建一个新文件。 This is done on the background thread, as it should be done.这是在后台线程上完成的,因为它应该这样做。

I have also created a callback interface to get the "status" and display a ProgressBar我还创建了一个回调接口来获取“状态”并显示一个ProgressBar

Here is the interface(You can change the name and callbacks to whatever):这是界面(您可以将名称和回调更改为任何内容):

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

Here is the class to copy the file:这是复制文件的类:

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

In your Activity you should do the following:在您的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();
        }
        
    }
}

You can cancel the copying of the file at any time, by calling:您可以随时取消文件的复制,方法是调用:

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

If you want to implement it exactly as I did and display a ProgressBar while the file is being copied, then here is the layout and style of my dialog:如果您想完全按照我的做法实现它并在复制文件时显示ProgressBar ,那么这里是我的对话框的布局和样式:

R.style.myDialog : 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 : 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>

Here is the dialog while the file is being copied:这是复制文件时的对话框:

例子


Conclusion:结论:

With the above, your file will be copied to /storage/emulated/0/Android/data/yourPackageName/files/Temp/YourFile.jpg .使用上述内容,您的文件将被复制到/storage/emulated/0/Android/data/yourPackageName/files/Temp/YourFile.jpg While the files are being copied a progress dialog will be displayed indicating the progress as a percentage (this is useful for when copying large files).在复制文件时,将显示一个进度对话框,以百分比表示进度(这对于复制大文件很有用)。 If there was an error while copying the file, the reason will be provided in onCopyPostExecute如果复制文件时出错,原因将在onCopyPostExecute中提供

This will work with file and content Uri's.这将适用于文件和内容 Uri。

You can try this;你可以试试这个;

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

and define FilePath class like this;并像这样定义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());
}
}

One line solution for your problem一条线解决您的问题

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

You missed你错过了

.getPath()

you can use this funtion for get file from uri in new android and older:您可以使用此功能从新 android 和旧版本的 uri 中获取文件:

fun getFileFromUri(context: Context, uri: Uri?): File? {
    uri ?: return null
    uri.path ?: return null

    var newUriString = uri.toString()
    newUriString = newUriString.replace(
        "content://com.android.providers.downloads.documents/",
        "content://com.android.providers.media.documents/"
    )
    newUriString = newUriString.replace(
        "/msf%3A", "/image%3A"
    )
    val newUri = Uri.parse(newUriString)

    var realPath = String()
    val databaseUri: Uri
    val selection: String?
    val selectionArgs: Array<String>?
    if (newUri.path?.contains("/document/image:") == true) {
        databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        selection = "_id=?"
        selectionArgs = arrayOf(DocumentsContract.getDocumentId(newUri).split(":")[1])
    } else {
        databaseUri = newUri
        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 = realPath.ifEmpty {
        when {
            newUri.path?.contains("/document/raw:") == true -> newUri.path?.replace(
                "/document/raw:",
                ""
            )
            newUri.path?.contains("/document/primary:") == true -> newUri.path?.replace(
                "/document/primary:",
                "/storage/emulated/0/"
            )
            else -> return null
        }
    }
    return if (path.isNullOrEmpty()) null else File(path)
}

How to create file using android Uri如何使用android Uri创建文件

It may seem difficult to create a java.io.File from an android.net.Uri , since there is no direct way to convert an android.net.Uri into java.net.URI .android.net.Uri创建java.io.File似乎很困难,因为没有直接的方法可以将android.net.Uri转换为java.net.URI But if you have the ApplicationContext you can do it very easily.但是如果你有ApplicationContext你可以很容易地做到这一点。

Here's how to do it from inside a fragment class.以下是如何从片段类中执行此操作。

fun createFile(uri: Uri) {
    try {
requireContext().applicationContext.contentResolver.openFileDescriptor(uri, "w")?.use { fd ->
        FileOutputStream(fd).use { fos ->

            // do your job on the FileOutputStream
            // also use background thread

            fos.close()
        }
    }
  } catch (e: Exception) {

  }
}

Note: File operations throws multiple exceptions, so handle them carefully.注意:文件操作会抛出多个异常,请谨慎处理。 And also do file operations in worker threads.并且还在工作线程中进行文件操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM