Api Interface :
@Multipart
@POST(AppConstants.UPDATE_PRODUCT)
fun updateProduct(
@Header("Authorization") auth: String,
@Part("product_id") id: RequestBody,
@Part("title") title: RequestBody,
@Part("description") description: RequestBody,
@Part("price") price: RequestBody,
@Part image: MultipartBody.Part
) : Call<UpdateProductResponse>?
Intent :
val photoIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "image/*"
addCategory(Intent.CATEGORY_OPENABLE)
}
startActivityForResult(photoIntent, UPLOAD_IMAGE)
Activity result :
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == UPLOAD_IMAGE && resultCode == RESULT_OK) {
if (data != null) {
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, data.data)
update_iv.setImageBitmap(bitmap)
val imageType = contentResolver.getType(data.data!!)
val extension = imageType!!.substring(imageType.indexOf("/") + 1)
val file = File(getPath(data.data!!, this)!!)
//val byte = contentResolver.openInputStream(data.data!!)
//val contentPart = InputStreamRequestBody("image/*".toMediaType() , contentResolver , data.data!!)
//filePartImage = MultipartBody.Part.createFormData("image" , file.name , contentPart)
data.data!!.let {
application.contentResolver.openInputStream(it)?.use { inputStream ->
filePartImage = MultipartBody.Part.createFormData(
"image",
"" + file.name, //or "image.$extension" none of the two works
inputStream.readBytes().toRequestBody("image/*".toMediaType())
)
}
}
showMessage(this, "" + getString(R.string.image_uploaded))
} else {
showMessage(this, "" + "File upload failed!")
}
}
}
Api call : Here filePartImage is global variable like this : private var filePartImage: MultipartBody.Part? = null
RetrofitClientInstance().createService(ArtOFiestaServiceClass::class.java).updateProduct(
"Bearer " + Prefs.getString("token", ""),
id.trim().toRequestBody("text/plain".toMediaType()),
update_name.text.toString().trim().toRequestBody("text/plain".toMediaType()),
update_desc.text.toString().trim().toRequestBody("text/plain".toMediaType()),
update_price.text.toString().trim().toRequestBody("text/plain".toMediaType()),
filePartImage!!
)?.enqueue(object : retrofit2.Callback<UpdateProductResponse> {
override fun onResponse(
call: Call<UpdateProductResponse>,
response: Response<UpdateProductResponse>
)
Manifest :
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
android:requestLegacyExternalStorage="true"
I have developed it's backend in node js and using multer to upload files. I can upload a file using postman but fail from my mobile application. What is wrong with my code ? Could it be android Scoped Storage issue ?
The problem is due to Scoped Storage
, because I am also facing the same problem as well.
My solution is convert inputStream
to identical File
and store it somewhere else. Then after upload, you may delete the file.
The idea is as below.
contentResolver.openInputStream(it)?.use { inputStream ->
// STEP 1: Create a tempFile for storing the image from scoped storage.
val tempFile = createTempFile(this, fileName, extension)
// STEP 2: copy inputStream into the tempFile
copyStreamToFile(it, tempFile)
// STEP 3: get file path from tempFile for further upload process.
val filePath = tempFile.absolutePath
val requestFile: RequestBody = File(filePath).asRequestBody(fileType?.toMediaTypeOrNull())
val body: MultipartBody.Part = MultipartBody.Part.createFormData("file", fileName, requestFile)
}
@Throws(IOException::class)
fun createTempFile(context: Context, fileName: String?, extension: String?): File {
val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
return File(storageDir, "$fileName.$extension")
}
fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
inputStream.use { input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}