-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://stackoverflow.com/a/62830720/12630878 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;
    }
Priyanka Singh
  • 40
  • 1
  • 14
  • How about removing @Headers("Content-Type: multipart/form-data")? – WHOA Jul 08 '20 at 02:01
  • @WHOA If I remove then this is coming: {"head":{"StatusValue":400,"StatusText":"Failed"},"body":{"Error":" must be object"}} – Priyanka Singh Jul 08 '20 at 06:21
  • @WHOA Please check the updated question – Priyanka Singh Jul 08 '20 at 07:13
  • did you using same exact value `fileId` in postman and your Android client ? – Rio A.P Jul 08 '20 at 07:26
  • @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' – Priyanka Singh Jul 08 '20 at 07:30
  • @RapSherlock If I'll use Query, instead of Part("body") JSONObject body, then it says must be object – Priyanka Singh Jul 08 '20 at 07:32
  • @PriyankaSingh check https://futurestud.io/tutorials/retrofit-2-how-to-upload-files-to-server. – Rajasekaran M Jul 08 '20 at 08:56
  • @RajasekaranM Exxactly implemented, but in my case JSON is there instead if String description. Can you guide me for that? – Priyanka Singh Jul 08 '20 at 09:39
  • we have to convert json as string . can you check my answer? @PriyankaSingh – Rajasekaran M Jul 08 '20 at 09:40
  • @RajasekaranM Okay, wait – Priyanka Singh Jul 08 '20 at 09:41
  • Of course, you can always debug HTTP calls by changing the endpoint to something like https://webhook.site/ and comparing both HTTP requests. – Jan Heinrich Reimer Jul 09 '20 at 10:24
  • @Heinrich Please check the edited question – Priyanka Singh Jul 09 '20 at 13:21
  • @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? – Sandeep Tiwari Jul 10 '20 at 11:28
  • @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://stackoverflow.com/a/62830720/12630878 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: https://drive.google.com/file/d/1S5-Sdp_CCSXXsOsZxwuhv0F5_CtH2EQO/view?usp=sharing – Priyanka Singh Jul 10 '20 at 12:07
  • @SandeepTiwari I briefly edited the question. Please read the complete question. – Priyanka Singh Jul 10 '20 at 12:24
  • You can get file uri and convert that uri into inputstream and upload for more check my question on which you commented – Sandeep Tiwari Jul 10 '20 at 12:46
  • @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. – Priyanka Singh Jul 10 '20 at 12:48
  • Use model instead of json string in request – MathankumarK Jul 10 '20 at 13:28
  • @MathankumarK 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? – Priyanka Singh Jul 10 '20 at 13:29
  • @PriyankaSingh Are you sure the above code is not working only for the pdf? – MathankumarK Jul 10 '20 at 13:56
  • @MathankumarK 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://stackoverflow.com/a/62830720/12630878 ], that is working till Oreo version. But I want solution for P, Q and R as well. – Priyanka Singh Jul 10 '20 at 13:59
  • Get rid of all the "path" stuff if you are trying to upload content from a `Uri`. See [this blog post](https://commonsware.com/blog/2020/07/05/multipart-upload-okttp-uri.html) and [this blog post](https://cketti.de/2020/05/23/content-uris-and-okhttp/) for how to do a multipart form uploading using a `Uri`. – CommonsWare Jul 10 '20 at 15:56
  • @CommonsWare 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. – Priyanka Singh Jul 10 '20 at 16:03
  • @PriyankaSingh: Use the second code listing in [this OkHttp issue comment](https://github.com/square/okhttp/issues/3585#issuecomment-327319196), which is in Java. – CommonsWare Jul 10 '20 at 16:39
  • 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 https://stackoverflow.com/questions/13209494/how-to-get-the-full-file-path-from-uri – AMAN SINGH Jul 12 '20 at 06:03
  • 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. – Rahul Singh Chandrabhan Aug 02 '20 at 07:23

2 Answers2

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

Rajasekaran M
  • 2,478
  • 2
  • 20
  • 30
-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();
    }
Rahul sharma
  • 1,492
  • 12
  • 26