问题
I am working on android app and I need to post request with file to the server.
I am using retrofit
to do that and I do Multipart
api request.
Then I use Intent.createChooser
to pick the file.
The problem come in when I do enqueue
to the service call. In the onFailure
I get this error:
E/Upload errorrrrrr:: /document/image:77317 (No such file or directory)
However, This is the uri and file path which I get in the onActivityResult
:
uri: content://com.android.providers.media.documents/document/image%3A77317
path: /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:
E/on getPath: edit profile java.lang.IllegalArgumentException: column '_data' does not exist. 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)
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>
回答1:
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.
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.
回答2:
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
}
}
来源:https://stackoverflow.com/questions/55351763/getting-error-no-such-file-or-directory-while-uploading-file-with-retrofit