简体   繁体   English

上载经过改造的文件时出现错误(无此文件或目录)

[英]Getting error (No such file or directory) while uploading file with retrofit

I am working on android app and I need to post request with file to the server. 我正在使用android应用,需要将带有文件的请求发布到服务器。 I am using retrofit to do that and I do Multipart api request. 我正在使用retrofit来做到这一点,并且我做了Multipart api请求。 Then I use Intent.createChooser to pick the file. 然后,我使用Intent.createChooser来选择文件。

The problem come in when I do enqueue to the service call. 当我加入服务呼叫enqueue时,问题就来了。 In the onFailure I get this error: onFailure此错误:

E/Upload errorrrrrr:: /document/image:77317 (No such file or directory) E / Upload errorrrrrr :: / document / image:77317(无此文件或目录)

However, This is the uri and file path which I get in the onActivityResult : 但是,这是我在onActivityResult获得的uri和文件路径:

uri: content://com.android.providers.media.documents/document/image%3A77317 uri: content://com.android.providers.media.documents/document/image%3A77317

path: /document/image:77317 路径: /document/image:77317

and I put the permissions: 我把权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera2.full" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
    android:name="android.hardware.camera.any"
    android:required="true" />

and this is the request: 这是请求:

@Multipart
@POST("upload_document")
Call<UploadDocuments> uploadDocuments(
        @Header("Authorization") String authorization,
        @Part MultipartBody.Part document_name,
        @Part("document_type") RequestBody document_type,
        @Part("fk_id") RequestBody fk_id,
        @Part("type") RequestBody type,
        @Part("certificate_name") RequestBody certificate_name,
        @Part("certificate_description") RequestBody certificate_description,
        @Part("notes") RequestBody notes);

How I can solve this problem? 我该如何解决这个问题? and Thanks, 谢谢

My Code: 我的代码:

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

    button = (Button) findViewById(R.id.btnUpload);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            Intent intent = new Intent();
            intent.setType("*/*");
            intent.setAction(Intent.ACTION_GET_CONTENT);
            startActivityForResult(Intent.createChooser(intent, "Select File"), PICK_PHOTO_FOR_AVATAR );

        }
    });
}



@Override
protected void onActivityResult(int requestCode, final int resultCode, Intent data) {

    if (requestCode == PICK_PHOTO_FOR_AVATAR && resultCode == Activity.RESULT_OK) {
        if (data == null) {
            //Display an error
            return;
        }
        else {
            Uri uri = data.getData();
            path = data.getData().getPath();
            if(uri != null)
            uploadFile(uri);

        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}



private void uploadFile(Uri fileUri) {
    // use the FileUtils to get the actual file by uri
    File file = FileUtils.getFile( fileUri.getPath());

    // create RequestBody instance from file
    final RequestBody requestFile =
            RequestBody.create(
                    MediaType.parse("*/*"),
                    file
            );

    // MultipartBody.Part is used to send also the actual file name
    final MultipartBody.Part body =
            MultipartBody.Part.createFormData("document_name", file.getName(), requestFile);

    // add another part within the multipart request
    String document_type1 = "CV";
    String fk_id1 ="2";
    String type1 = "property_documents";
    String certificate_name1 = "anyname";
    String certificate_description1 = "anyDesc";
    String notes1 =  "anyNotes";

    RequestBody document_type =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, document_type1);
    RequestBody fk_id =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, fk_id1);
    RequestBody type =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, type1);
    RequestBody certificate_name =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, certificate_name1);
    RequestBody certificate_description =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, certificate_description1);
    RequestBody notes =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, notes1);



    // finally, execute the request
    Call<UploadDocuments> call = service.uploadDocuments(
            authorization,body,document_type,
            fk_id,type,certificate_name,
            certificate_description,notes);
    call.enqueue(new Callback<UploadDocuments>() {

        @Override
        public void onResponse(Call<UploadDocuments> call,Response<UploadDocuments> response) {

            Log.v("Upload", "successssssssssss");
            if(response.isSuccessful()) {
                String status = response.body().getMessage();
                if (status.equals("success")) {
                    String name = response.body().getData().getCertificateName();
                    Toast.makeText(getApplicationContext(),"done " + name,Toast.LENGTH_LONG).show();

                }
            }


        }

        @Override
        public void onFailure(Call<UploadDocuments> call, Throwable t) {
            Log.e("Upload errorrrrrr:", t.getMessage());
        }
    });

}

Edit: 编辑:

Also I have additional problem after editing my code as Jeel answer, The problem when I take image capture I get this error: 另外,在将代码编辑为Jeel答案后,我还有其他问题,拍摄图像时出现此错误:

E/on getPath: edit profile java.lang.IllegalArgumentException: column '_data' does not exist. E / on getPath:编辑配置文件java.lang.IllegalArgumentException:列'_data'不存在。 Available columns: [] at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:340) at android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:87) at com.example.android.renteragentapp.API_Utility.FileUtil.getPath(FileUtil.java:65) at com.example.android.renteragentapp.Activity.ScanPassportActivity.uploadFile(ScanPassportActivity.java:195) 可用的列:[]位于android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:340)位于android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:87)位于com.example.android.renteragentapp.API_Utility.FileUtil.getPath( FileUtil.java:65),位于com.example.android.renteragentapp.Activity.ScanPassportActivity.uploadFile(ScanPassportActivity.java:195)

  button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {


          if(hasStoragePermission(101)){
                Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                // Create the File where the photo should go
                File photoFile = null;
                try {
                    photoFile = createImageFile();
                } catch (IOException ex) {
                    // Error occurred while creating the File

                }
                // Continue only if the File was successfully created
                if (photoFile != null) {
                    if ((Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT)) {
                        photoURI = FileProvider.getUriForFile(ScanPassportActivity.this,
                                "com.example.provider",
                                photoFile);
                        //FAApplication.setPhotoUri(photoURI);
                    } else {
                        photoURI = Uri.fromFile(photoFile);
                    }

                    takePicture.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1)
                    {
                        takePicture.putExtra("android.intent.extras.LENS_FACING_FRONT", 1);
                    }
                    else {
                        takePicture.putExtra("android.intent.extras.CAMERA_FACING", 1);
                    }
                    startActivityForResult(takePicture, 101);
                }

          }

        }
    });

  private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

    File file = File.createTempFile(
            imageFileName,   //prefix
            ".jpg",          //suffix
            storageDir       //directory
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = file.getAbsolutePath();
    return file;
}
private boolean hasStoragePermission(int requestCode) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
            return false;
        } else if( checkSelfPermission(Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, requestCode);

            return false;
        }
        else {
            return true;
        }
    } else {
        return true;
    }
}

also I set this provider: 我也设置了这个提供者:

  <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.provider"
        android:exported="false"
        android:grantUriPermissions="true"
        tools:replace="android:authorities">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"
            tools:replace="android:resource" />

    </provider>

Your issue is that for API level 19 & above (Android Kitkat) , if you open documents provider for your file picker then Uri you get back differs depending from where you pick file. 您的问题是,对于API级别19及更高版本(Android Kitkat) ,如果您为文件选择器打开文档提供程序,那么Uri取回取决于您选择文件的位置。

Use this class: 使用此类:

public class FileUtil {
    /*
     * Gets the file path of the given Uri.
     */
    @SuppressLint("NewApi")
    public static String getPath(Uri uri, Context context) {
        final boolean needToCheckUri = Build.VERSION.SDK_INT >= 19;
        String selection = null;
        String[] selectionArgs = null;
        // Uri is different in versions after KITKAT (Android 4.4), we need to
        // deal with different Uris.
        if (needToCheckUri && DocumentsContract.isDocumentUri(context, uri)) {
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            } else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                if (id.startsWith("raw:")) {
                    return id.replaceFirst("raw:", "");
                }
                uri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
            } else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                switch (type) {
                    case "image":
                        uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                        break;
                    case "video":
                        uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                        break;
                    case "audio":
                        uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                        break;
                }
                selection = "_id=?";
                selectionArgs = new String[]{
                    split[1]
                };
            }
        }
        if ("content".equalsIgnoreCase(uri.getScheme())) {
            String[] projection = {
                MediaStore.Images.Media.DATA
            };
            try (Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null)) {
                if (cursor != null && cursor.moveToFirst()) {
                    int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                    return cursor.getString(columnIndex);
                }
            } catch (Exception e) {
                Log.e("on getPath", "Exception", e);
            }
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    private 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.
     */
    private 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.
     */
    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
}

And obtain your file like below: 并获取您的文件,如下所示:

File file = new File(FileUtil.getPath(uri, this)); // *this* here is context, which can be Activity/Fragment

Note: Let me know if someone need explanation about code. 注意: 让我知道是否有人需要有关代码的解释。

You have to ask runtime permission like below before you select image from Gallery. 在从图库中选择图像之前,您必须先询问以下运行时权限。

private boolean hasStoragePermission(int requestCode) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (grantResults.length > 0
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // Permission Granted

    }
}

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

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