I've made multipart request to server for sending images from phone gallery according to tutorial, but I don't know what is wrong with it. Server will return 201 response code, but image is not sent correctly to the server.
Here is the HTTPRequest function for sending the image.
fun doPostImage(resources: String, avatarImage: ByteArray, fileName: String): JSONObject{
val url = SERVER_URL + resources
val maxBufferSize = 1024 * 1024
val X_SESSION: String? = context?.getSharedPreferences(context.
getString(R.string.SharedPrefs), MODE_PRIVATE)?.
getString(context.getString(R.string.X_SESSION), null)
createLog("AvatarRequestURL", "POST $url")
val obj = URL(url)
val attachmentName = fileName
val attachmentFileName = fileName + ".jpg"
val crlf = "\r\n"
val twoHyphens = "--"
val boundary = "*****"
with(obj.openConnection() as HttpURLConnection) {
requestMethod = "POST"
doOutput = true
doInput = true
val postData: ByteArray = avatarImage
if (X_SESSION != null){
createLog("DOPOST-SESSION", X_SESSION)
setRequestProperty("X-Session", X_SESSION)
} else {
createLog("DOPOST-SESSION", "SESSION NULL :(")
}
setRequestProperty("Connection", "Keep-Alive")
setRequestProperty("Cache-Control", "no-cache")
setRequestProperty("X-API-Key", API_KEY)
setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary)
setChunkedStreamingMode(maxBufferSize)
//constructing request body
try {
val writer = PrintWriter(OutputStreamWriter(outputStream, "UTF-8"), true)
writer.append(twoHyphens + boundary + crlf)
writer.append("Content-Disposition: form-data; name=\"" + attachmentName + "\";filename=\"" + attachmentFileName + "\"" + crlf)
writer.append("Content-Type", "image/jpeg")
writer.append(crlf)
writer.flush()
val byteArrayInputStream = ByteArrayInputStream(postData)
byteArrayInputStream.copyTo(outputStream, maxBufferSize)
} catch (exception: Exception) {
createLog("AvatarImageException", exception.toString())
}
createLog("Avatar_Upload_Response", "Response Code Image: $responseCode")
//server response handling
if (responseCode != HttpURLConnection.HTTP_OK) {
try {
BufferedReader(InputStreamReader(errorStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
createLog("AvatarRequest", "Response body Image: $response")
return JSONObject(response.toString())
}
} catch (exception: Exception) {
return if(responseCode == HttpURLConnection.HTTP_CLIENT_TIMEOUT){
createLog("POST EXCEPTION", exception.toString())
val errorResponse = JSONObject()
errorResponse.put("ErrorCode", responseCode)
errorResponse.put("Message", exception.message)
errorResponse
} else {
createLog("EXCEPTION", exception.toString())
val errorResponse = JSONObject()
errorResponse.put("ErrorCode", responseCode)
errorResponse.put("Message", exception.message)
return errorResponse
}
}
} else {
try {
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
createLog("AvatarRequest", "Response body Image: $response")
return JSONObject(response.toString())
}
} catch (exception: Exception) {
throw Exception("Exception while push the notification $exception")
}
}
}
}
Here how I get image from gallery:
profilePhotoSelectButton.setOnClickListener {
val galleryIntent = Intent()
galleryIntent.type = "image/*"
galleryIntent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(galleryIntent, "Choose profile photo"), PICK_PHOTO)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == PICK_PHOTO){
val avatarPhoto: Uri? = data?.data
if (avatarPhoto != null){
val imageStream: InputStream = contentResolver.openInputStream(avatarPhoto)
val imageBitmap: Bitmap = BitmapFactory.decodeStream(imageStream)
val roundedBitMapDrawable: RoundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, imageBitmap)
roundedBitMapDrawable.isCircular = true
profilePhotoPreview.setImageDrawable(roundedBitMapDrawable)
val userPhotoToJSON: Bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, avatarPhoto)
val resizedBitmap: Bitmap = getResizedBitmap(userPhotoToJSON, 150)
userProfilePhotoEncoded = compressBitmap(resizedBitmap, Bitmap.CompressFormat.JPEG)
userProfilePhotoPath = avatarPhoto.path
createLog("AvatarPath ", userProfilePhotoPath.toString())
userProfilePhotoName = File(avatarPhoto.path).name
createLog("AvatarName ", userProfilePhotoName.toString())
}
}
}
private fun compressBitmap(bitmapPicture: Bitmap, compressFormat: Bitmap.CompressFormat): ByteArray {
val COMPRESSION_QUALITY = 50
val byteArrayBitmapStream = ByteArrayOutputStream()
bitmapPicture.compress(compressFormat, COMPRESSION_QUALITY, byteArrayBitmapStream)
val avatarByteArray: ByteArray = byteArrayBitmapStream.toByteArray()
return avatarByteArray
}
/**
* reduces the size of the image
* @param image
* @param maxSize
* @return
*/
fun getResizedBitmap(image: Bitmap, maxSize: Int): Bitmap {
var width = image.width
var height = image.height
val bitmapRatio = width.toFloat() / height.toFloat()
if (bitmapRatio > 1) {
width = maxSize
height = (width / bitmapRatio).toInt()
} else {
height = maxSize
width = (height * bitmapRatio).toInt()
}
return Bitmap.createScaledBitmap(image, width, height, true)
}
api.updateAvatar(userProfilePhotoEncoded!!,userProfilePhotoPath!!, userProfilePhotoName!!, object: IProfileCallback{
override fun onError(errorJSON: JSONObject) {
createLog("AVATAR_ERROR ", errorJSON.toString())
}
override fun onSuccess(profileJSON: JSONObject?) {
createLog("AVATAR SUCCESS", profileJSON.toString())
setResult(MYDATA_UPDATE_RESULT)
finish()
}
})
fun updateAvatar(avatar: ByteArray, filepath: String, fileName: String, callback: IProfileCallback){
class UpdateProfileAsync(private val profileCallback: IProfileCallback): AsyncTask<Void, Void, JSONObject>() {
override fun doInBackground(vararg p0: Void?): JSONObject {
val server = Server.getInstance(context!!)
return server.doPostImage(context.getString(R.string.profile_avatar_link), avatar, fileName)
}
override fun onPostExecute(result: JSONObject?) {
super.onPostExecute(result)
if(result!!.has("ErrorCode")){
profileCallback.onError(result)
} else {
createLog("SuccessfullyUpdatedProfile", "onSuccess()")
profileCallback.onSuccess(result)
}
}
}
UpdateProfileAsync(callback).execute()
}