Why does PDF is not able to upload in PHP API for Android Pie, Q and R using Retrofit 2 in Android/Java? [Not solved]
Asked Answered
M

2

-4

I tried to upload a PDF file [Not Image] in PHP API from Android, and I'm getting 400 with this exception:

{"head":{"StatusValue":400,"StatusText":"Failed"},"body":{"Error":"Missing required property fileId"}}

I'm getting 200 request code, when I'm doing in Postman:

enter image description here

Now, Android code:

    @Multipart
    @POST("eligibity/auth/attachment/upload/add")
    Call<ResponseBody> uploadFile(@Part MultipartBody.Part file, @Part("fileName") RequestBody name, @Part("body") JSONObject body);

 
 private void uploadPDF(String path) {

        String pdfname = String.valueOf(Calendar.getInstance().getTimeInMillis());

        File file = new File(path);
        RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("fileName", file.getName(), requestBody);
        RequestBody filename = RequestBody.create(MediaType.parse("text/plain"), pdfname);

        try {
            JSONObject sJsonAttachment = new JSONObject();
            JSONObject body = new JSONObject();
            sJsonAttachment.put("appointmentId", AdapterCollections.clsClaimDiscussionList.get(position).appointment_id);
            sJsonAttachment.put("createdBy", Integer.parseInt(preferenceManager.getUserId()));
            sJsonAttachment.put("customerId", Integer.parseInt(preferenceManager.getCustomerId()));
            sJsonAttachment.put("encounterId", AdapterCollections.clsClaimDiscussionList.get(position).encounter_id);
            sJsonAttachment.put("expiryDate", Utility.getCurrentDate("MM-DD-YY"));
            sJsonAttachment.put("fbType", 4);
            sJsonAttachment.put("fileId", 0);
            sJsonAttachment.put("fileName", "name");
            sJsonAttachment.put("insTpaPatId", 0);
            sJsonAttachment.put("isActive", 1);
            sJsonAttachment.put("messageId", AdapterCollections.clsClaimDiscussionList.get(position).log_messages.get(position).log_id);
            sJsonAttachment.put("patientId", AdapterCollections.clsClaimDiscussionList.get(position).patient_id);
            sJsonAttachment.put("recType", "");
            sJsonAttachment.put("reportDate", Utility.getCurrentDate("DD-MM-YYYY"));
            sJsonAttachment.put("siteId", Integer.parseInt(preferenceManager.getSiteId()));
            sJsonAttachment.put("type", 4);
            sJsonAttachment.put("uploadDate", Utility.getCurrentDate("MMDDYY"));

           // body.put("body", sJsonAttachment);

            ApiCall apiCall = RetrofitService.createService(SCMS_BASE_URL, ApiCall.class);
            assert apiCall != null;
            apiCall.uploadFile(fileToUpload, filename, sJsonAttachment).enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(@NotNull Call<ResponseBody> call, @NotNull Response<ResponseBody> response) {
                    try {
                        showLog("STATUS: " + response.code());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onFailure(@NotNull Call<ResponseBody> call, @NotNull Throwable t) {
                    showLog("FAILED: "+ t.getMessage());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

I know that I'm doing somewhere small mistakes, but not able to spot that.

If I used part then this error: Missing required property fileId, and if I use query then this error: must be object.

Update

Regarding Path:

provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="/storage/emulated/0" path="."/>
</paths>

Intent to open PDF chooser:

 private void fileDialog() {
    Intent intent = new Intent().setType("application/pdf").setAction(Intent.ACTION_GET_CONTENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    
    startActivityForResult(Intent.createChooser(intent, "Select PDF file"), 123);
}

I followed this https://mcmap.net/q/161187/-why-does-pdf-is-not-able-to-upload-in-php-api-for-android-pie-q-and-r-using-retrofit-2-in-android-java-not-solved answer to getPath, but, I'm NOT getting path properly.

Higher version means: 28 to 30

Higher Version URI: URI: content://com.android.providers.media.documents/document/document%3A31

FROM RECENT, If I'll choose PDF, then cursor is returning null:

 if (cursor.moveToFirst()) {
        return cursor.getString(column_index);
        }

FROM DOWNLOADS, if I'll choose PDF, then NumberFormatException coming on this line: URI: content://com.android.providers.downloads.documents/document/msf%3A27

Exception: java.lang.NumberFormatException: For input string: "msf:27"

LINE NO: Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));

FileUtils class: https://drive.google.com/file/d/1S5-Sdp_CCSXXsOsZxwuhv0F5_CtH2EQO/view?usp=sharing

Search this method where I'm getting Path:

public static File getFile(Context context, Uri uri) {
        if (uri != null) {
            String path = getPathForAllVersion(context, uri);
            //   String path = getFilePathFromURI(context, uri);
            showLog("isPATH: " + path);
            if (checkNull(path) && isLocal(path)) {
                return new File(path);
            }
        }
        return null;
    }
Marylou answered 7/7, 2020 at 20:30 Comment(26)
How about removing @Headers("Content-Type: multipart/form-data")?Privy
@Privy If I remove then this is coming: {"head":{"StatusValue":400,"StatusText":"Failed"},"body":{"Error":" must be object"}}Marylou
@Privy Please check the updated questionMarylou
did you using same exact value fileId in postman and your Android client ?Indolence
@RapSherlock Exact same, even whatever JSON is going from Abdroid, that I debug - copy value and pasted in Postman in "body" - value and checked, in postman it is coming success'Marylou
@RapSherlock If I'll use Query, instead of Part("body") JSONObject body, then it says must be objectMarylou
@PriyankaSingh check futurestud.io/tutorials/….Scintillate
@RajasekaranM Exxactly implemented, but in my case JSON is there instead if String description. Can you guide me for that?Marylou
we have to convert json as string . can you check my answer? @PriyankaSinghScintillate
@RajasekaranM Okay, waitMarylou
Of course, you can always debug HTTP calls by changing the endpoint to something like webhook.site and comparing both HTTP requests.Eugenieeugenio
@Heinrich Please check the edited questionMarylou
@PriyankaSingh before jump into your problem, could you please clear some point , How are you getting file Path? Which android Version are you using? Are you getting valid file path?Disengagement
@SandeepTiwari Till Nougat version, I'm getting path using URI. Please search this method getFilePathFromURI() ..... After Nougat I'm not getting path and it is crashing. So After Nougat I tried with this solution: https://mcmap.net/q/161187/-why-does-pdf-is-not-able-to-upload-in-php-api-for-android-pie-q-and-r-using-retrofit-2-in-android-java-not-solved but here I'm getting Path null. That I already mentioned in Question. I've tested in Oreo, Pie, Q and R. It's not working in any version. To check the FileUtils class, please see: drive.google.com/file/d/1S5-Sdp_CCSXXsOsZxwuhv0F5_CtH2EQO/…Marylou
@SandeepTiwari I briefly edited the question. Please read the complete question.Marylou
You can get file uri and convert that uri into inputstream and upload for more check my question on which you commentedDisengagement
@SandeepTiwari Please post an answer. As I tried so many things. I might be doing wrong. That's why asking you to post an answer.Marylou
Use model instead of json string in requestGothurd
@Gothurd Only single text is coming, which is saying "Uploaded successfully" So it's fine. BTW Thanks for the comment. Can you please try to understand and give me some suggestions that I can try?Marylou
@PriyankaSingh Are you sure the above code is not working only for the pdf?Gothurd
@Gothurd Point 1: I'm only testing PDF, because only PDF I have to send in API, so don't know about other Media types. Point 2: In question, I mentioned one answer link [ https://mcmap.net/q/161187/-why-does-pdf-is-not-able-to-upload-in-php-api-for-android-pie-q-and-r-using-retrofit-2-in-android-java-not-solved ], that is working till Oreo version. But I want solution for P, Q and R as well.Marylou
Get rid of all the "path" stuff if you are trying to upload content from a Uri. See this blog post and this blog post for how to do a multipart form uploading using a Uri.Thames
@Thames As I don't know Kotlin, Please post an answer in Java according to my question. Because the original question I asked, that is PDF and along with JSON I need to pass.Marylou
@PriyankaSingh: Use the second code listing in this OkHttp issue comment, which is in Java.Thames
Check this answer for getting the path as you updated, you're facing some issue in getting the path and let me know if you're still facing the issue #13209994Faraday
Don't send the body in multipart, only PDF file will go in that, use HashMap, or Pojo file for request body. If need more help then let me know.Annatto
S
2

You api accept only two parameters but you passed three parameters that's why you getting error.

so API method should be

@Multipart
@POST("eligibity/auth/attachment/upload/add")
Call<ResponseBody> uploadFile(
    @Part("body") RequestBody description,
    @Part MultipartBody.Part file
);

And update your uploadPDF() like below

    private void uploadPDF(String path) {

   //json data
    JSONObject sJsonAttachment = new JSONObject();
            JSONObject body = new JSONObject();
            sJsonAttachment.put("appointmentId", AdapterCollections.clsClaimDiscussionList.get(position).appointment_id);
            sJsonAttachment.put("createdBy", Integer.parseInt(preferenceManager.getUserId()));
            sJsonAttachment.put("customerId", Integer.parseInt(preferenceManager.getCustomerId()));
            sJsonAttachment.put("encounterId", AdapterCollections.clsClaimDiscussionList.get(position).encounter_id);
            sJsonAttachment.put("expiryDate", Utility.getCurrentDate("MM-DD-YY"));
            sJsonAttachment.put("fbType", 4);
            sJsonAttachment.put("fileId", 0);
            sJsonAttachment.put("fileName", "name");
            sJsonAttachment.put("insTpaPatId", 0);
            sJsonAttachment.put("isActive", 1);
            sJsonAttachment.put("messageId", AdapterCollections.clsClaimDiscussionList.get(position).log_messages.get(position).log_id);
            sJsonAttachment.put("patientId", AdapterCollections.clsClaimDiscussionList.get(position).patient_id);
            sJsonAttachment.put("recType", "");
            sJsonAttachment.put("reportDate", Utility.getCurrentDate("DD-MM-YYYY"));
            sJsonAttachment.put("siteId", Integer.parseInt(preferenceManager.getSiteId()));
            sJsonAttachment.put("type", 4);
            sJsonAttachment.put("uploadDate", Utility.getCurrentDate("MMDDYY"));




   // create RequestBody instance from file
    File file=new File(path);
    RequestBody requestFile =
            RequestBody.create(
                         MediaType.parse(Files.probeContentType(file.toPath()))),
                         file
             );

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

    // add another part within the multipart request
     RequestBody bodyJsonAttachment =
            RequestBody.create(
                    okhttp3.MultipartBody.FORM, sJsonAttachment.toString());


    ApiCall apiCall = RetrofitService.createService(SCMS_BASE_URL, ApiCall.class);
            assert apiCall != null;
            apiCall.uploadFile(fileBody, bodyJsonAttachment).enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(@NotNull Call<ResponseBody> call, @NotNull Response<ResponseBody> response) {
                    try {
                        showLog("STATUS: " + response.code());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onFailure(@NotNull Call<ResponseBody> call, @NotNull Throwable t) {
                    showLog("FAILED: "+ t.getMessage());
                }
            });

 }

Note : if you getting warning error on mime type, update it

Scintillate answered 8/7, 2020 at 9:21 Comment(7)
compiler error: // create RequestBody instance from fileMarylou
remove one ) in end of toPath()Scintillate
update RequestBody requestFile = RequestBody.create( MediaType.parse(Files.probeContentType(file.toPath())), file );Scintillate
call requires api 26, but mine is 21Marylou
use static mime type instead of Files.probeContentType(file.toPath())Scintillate
Let us continue this discussion in chat.Scintillate
@PriyankaSingh - Sorry for delay reply.. hope you've got solution for your query..!Boyhood
M
-1

If you are using multipart then pass all the field in Multipart just like this.

Declare method in Api interface just like this. Only one MultipartBody field.

@Multipart
@POST("eligibity/auth/attachment/upload/add")
Call<ResponseBody> uploadFile(@Body MultipartBody body);

Create MultipartBody.Builder add your and your field in it.

    MultipartBody.Builder requestBodyBuilde = MultipartBody.Builder().setType(MultipartBody.FORM);
  
    //adding image in multipart
    File file = File(selected_file_path);
    RequestBody fileBody = ProgressRequestBody(file, this);
    requestBodyBuilde.addFormDataPart("fileName", file.name, fileBody);
  
    //add your other field just like this 
    requestBodyBuilde.addFormDataPart("body", sJsonAttachment );
  
 

   ApiCall apiCall = RetrofitService.createService(SCMS_BASE_URL, ApiCall.class);
        assert apiCall != null;
        //Note here we will pass only one parameter that is multipartBody 
        apiCall.uploadFile(requestBodyBuilde.build()).enqueue(new 
     Callback<ResponseBody>() {
            @Override
            public void onResponse(@NotNull Call<ResponseBody> call, @NotNull Response<ResponseBody> response) {
                try {
                    showLog("STATUS: " + response.code());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(@NotNull Call<ResponseBody> call, @NotNull Throwable t) {
                showLog("FAILED: "+ t.getMessage());
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
Mascarenas answered 8/7, 2020 at 8:2 Comment(7)
If I used part then this error: Missing required property fileId, and If I use query then this error: must be object.Marylou
First of all there is no token or something. Second, I want to upload PDF file, not image. Third, Please provide answer according to my questionMarylou
Compiler error: MultipartBody.Builder requestBodyBuilde: MultipartBody.Builder = MultipartBody.Builder().setType(MultipartBody.FORM);Marylou
File means any file including pdf .This example is about how you can upload file not an exact solution. You need to modify by yourselves . And this is syntax error because i converted kotlin code into java . sorry for that. I updated my answer.Mascarenas
That is what dear. I already made setup using lot of example. Now what I'm doing that suggestion is require from endMarylou
check now . I have made changes.Mascarenas
what provider ?Mascarenas

© 2022 - 2024 — McMap. All rights reserved.