0

I have tried many ways to upload an image to my node server but none of them worked. Finally, I came across the Ion library (https://github.com/koush/ion) to upload images but this is also not working. It reaches onCompleteListener but the image is not getting uploaded to MongoDB. It works fine in Postman but not in android. Here is the node code

    const multer= require('multer');
const storage = multer.diskStorage({

  destination: function(req, file, cb) {
    var tok = decode(req.params.id);
    // const url = req.protocol + "://" + req.get("host");
     let path = './images/'+tok.email;
      fs.mkdirsSync(path);
    cb(null, path);
  },
  filename: function(req, file, cb) {
    var tok = decode(req.params.id);
    cb(null, tok.email +'profilePic' + file.originalname);
  }
});
const upload = multer({
  storage: storage,
  limits: {
    fileSize: 1024 * 1024 * 1
  },
  fileFilter: fileFilter
});
app.post('/profilepic/:id',upload.single('profilePic'),function (req,res) {

  // if(!req.session.user){
  //   return res.status(200).send("failure@Not Authorized");
  // }
  var tok = decode(req.params.id);
  console.log(req.file.filename);
  User.updateOne({'_id': tok.id },{'profilePic':tok.email+'/'+req.file.filename}).then(result =>{
      console.log(result);
  if (result.n > 0) {
      res.status(200).json({ message: "success" });
      }else {
        res.status(200).json({ message: "failure@err in Updating pic" });
      }
    })
    .catch(error => {
      res.status(200).json({
        message: "failure@User not found!"
      });
    });

Here is my android code

    private void chooseImage(int imageReq) {
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(Intent.createChooser(intent, "Select Picture"), imageReq);
      }



      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PICK_IMAGE_REQUEST)
            && resultCode == RESULT_OK
            && data != null
            && data.getData() != null) {
          showProgressDialog();
          Uri filePath = data.getData();
          Log.d(TAG,getAbsolutePath(filePath));
          uploadFile(getAbsolutePath(filePath));

            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath);
            Bitmap lastBitmap = null;
            lastBitmap = bitmap;
            if (requestCode == PICK_IMAGE_REQUEST) {


              Glide.with(this)
                  .load(bitmap)
                  .into(profileCoverPic);
}

      }

     public String getAbsolutePath(Uri uri) {
        String[] projection = { MediaStore.MediaColumns.DATA };
        @SuppressWarnings("deprecation")
        Cursor cursor = managedQuery(uri, projection, null, null, null);
        if (cursor != null) {
          int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
          cursor.moveToFirst();
          return cursor.getString(column_index);
        } else
          return null;
      }

     private void uploadFile(String path){
        Ion.with(getApplicationContext())
            .load(profilePicUrl)
            .setMultipartParameter("name", "source")
            .setMultipartFile("profilepic", "image/jpeg", new File(path))
            .asJsonObject()
            .setCallback(new FutureCallback<JsonObject>() {
              @Override
              public void onCompleted(Exception e, JsonObject result) {
                hideProgressDialog();
                Log.d(TAG,"photo uploaded");
                Toast.makeText(ProfileActivity.this, "Photo uploaded", Toast.LENGTH_SHORT).show();
                //do stuff with result
              }
            });
      }

Any other method would also be appreciated.

Zohaib Amir
  • 3,482
  • 2
  • 11
  • 29
Sonu Sourav
  • 2,926
  • 2
  • 12
  • 25

2 Answers2

1

For image upload please use Volleymultipart class.

     package com.conceptioni.ashebbicom.util;

    import com.android.volley.AuthFailureError;
    import com.android.volley.NetworkResponse;
    import com.android.volley.ParseError;
    import com.android.volley.Request;
    import com.android.volley.Response;
    import com.android.volley.VolleyError;
    import com.android.volley.toolbox.HttpHeaderParser;

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.Map;

 public class VolleyMultipartRequest extends Request<NetworkResponse> {

 private final String twoHyphens = "--";
 private final String lineEnd = "\r\n";
 private final String boundary = "apiclient-" + System.currentTimeMillis();

 private Response.Listener<NetworkResponse> mListener;
 private Response.ErrorListener mErrorListener;
 private Map<String, String> mHeaders;


 public VolleyMultipartRequest(int method, String url,
                              Response.Listener<NetworkResponse> listener,
                              Response.ErrorListener errorListener) {
    super(method, url, errorListener);
    this.mListener = listener;
    this.mErrorListener = errorListener;
 }

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    return (mHeaders != null) ? mHeaders : super.getHeaders();
}

@Override
public String getBodyContentType() {
    return "multipart/form-data;boundary=" + boundary;
}

@Override
public byte[] getBody() throws AuthFailureError {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(bos);

    try {
        // populate text payload
        Map<String, String> params = getParams();
        if (params != null && params.size() > 0) {
            textParse(dos, params, getParamsEncoding());
        }

        // populate data byte payload
        Map<String, DataPart> data = getByteData();
        if (data != null && data.size() > 0) {
            dataParse(dos, data);
        }

        // close multipart form data after text and file data
        dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        return bos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}


protected Map<String, DataPart> getByteData() {
    return null;
}

 @Override
 protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response)  {
    try {
        return Response.success(
                response,
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (Exception e) {
        return Response.error(new ParseError(e));
    }
}

@Override
protected void deliverResponse(NetworkResponse response) {
    mListener.onResponse(response);
}

@Override
public void deliverError(VolleyError error) {
    mErrorListener.onErrorResponse(error);
}


  private void textParse(DataOutputStream dataOutputStream, Map<String, String> params, String encoding) throws IOException {
    try {
        for (Map.Entry<String, String> entry : params.entrySet()) {
            buildTextPart(dataOutputStream, entry.getKey(), entry.getValue());
        }
    } catch (UnsupportedEncodingException uee) {
        throw new RuntimeException("Encoding not supported: " + encoding, uee);
    }
}


  private void dataParse(DataOutputStream dataOutputStream, Map<String, DataPart> data) throws IOException {
     for (Map.Entry<String, DataPart> entry : data.entrySet()) {
        buildDataPart(dataOutputStream, entry.getValue(), entry.getKey());
     }
 }


   private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
    dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
    dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"" + lineEnd);
    dataOutputStream.writeBytes(lineEnd);
    dataOutputStream.writeBytes(parameterValue + lineEnd);
}


  private void buildDataPart(DataOutputStream dataOutputStream, DataPart dataFile, String inputName) throws IOException {
    dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
    dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" +
            inputName + "\"; filename=\"" + dataFile.getFileName() + "\"" + lineEnd);
    if (dataFile.getType() != null && !dataFile.getType().trim().isEmpty()) {
        dataOutputStream.writeBytes("Content-Type: " + dataFile.getType() + lineEnd);
    }
    dataOutputStream.writeBytes(lineEnd);

    ByteArrayInputStream fileInputStream = new   ByteArrayInputStream(dataFile.getContent());
    int bytesAvailable = fileInputStream.available();

    int maxBufferSize = 1024 * 1024;
    int bufferSize = Math.min(bytesAvailable, maxBufferSize);
    byte[] buffer = new byte[bufferSize];

    int bytesRead = fileInputStream.read(buffer, 0, bufferSize);

    while (bytesRead > 0) {
        dataOutputStream.write(buffer, 0, bufferSize);
        bytesAvailable = fileInputStream.available();
        bufferSize = Math.min(bytesAvailable, maxBufferSize);
        bytesRead = fileInputStream.read(buffer, 0, bufferSize);
    }

    dataOutputStream.writeBytes(lineEnd);
}

   public class DataPart {
    private String fileName;
    private byte[] content;
    private String type;

    public DataPart() {
    }

    public DataPart(String name, byte[] data) {
        fileName = name;
        content = data;
    }

    String getFileName() {
        return fileName;
    }

    byte[] getContent() {
        return content;
    }

    String getType() {
        return type;
    }

 }
 }

//////////////////// Use this class as below/////////////////////////

   VolleyMultipartRequest volleyMultipartRequest = new   VolleyMultipartRequest(Request.Method.POST, Constants.edit_photo,
            new Response.Listener<NetworkResponse>() {
                @SuppressLint("ApplySharedPref")
                @Override
                public void onResponse(NetworkResponse response) {
                    try {
                         //Response

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    pb.setVisibility(View.GONE);
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                }
            }) {


        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            params.put("API", "edit_photo");
           //all params here
            return params;
        }


        @Override
        protected Map<String, DataPart> getByteData() {
            Map<String, DataPart> params = new HashMap<>();
            long imagename = System.currentTimeMillis();
            params.put("var_image", new DataPart(imagename + ".png", getFileDataFromDrawable(bitmap)));
            return params;
        }
    };

    //adding the request to volley
            Volley.newRequestQueue(ActivityProfileDetailSlider.this).add(volleyMultipartRequest);

///////////////////////////////////////////////////////////////

 public byte[] getFileDataFromDrawable(Bitmap bitmap) {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 80, byteArrayOutputStream);
    return byteArrayOutputStream.toByteArray();
  }
Mayur Coceptioni
  • 433
  • 4
  • 17
  • I have seen this before, but the problem is the client is expecting network response on success. Could you please provide me the corresponding node server code for that? – Sonu Sourav Jul 23 '19 at 16:00
  • String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); – Mayur Coceptioni Jul 23 '19 at 16:08
  • No, I meant, I can convert network response to String in client-side but how to write code for it in the server-side. Please provide complete node server code for the multipart request. – Sonu Sourav Jul 23 '19 at 16:18
0

In your onCompleted() function, you need to check if there is an exception. Exception will give you details on why it is not working.

public void onCompleted(Exception e, JsonObject result) {
                hideProgressDialog();

                if(e != null){
                  //IN ERROR CASE
                   Log.d(TAG, e.getLocalizedMessage());
                  }else{

                Log.d(TAG,"photo uploaded");
                Toast.makeText(ProfileActivity.this, "Photo uploaded", 
                Toast.LENGTH_SHORT).show();
                //do stuff with result
                 }
              }
            });

Post the exception text in your post if you don't understand the error that gets printed in LogCat.

Zohaib Amir
  • 3,482
  • 2
  • 11
  • 29