I' trying to upload an image to server using retrofit2. I think that the way I send data is not correct but I haven't figure out yet where is my problem.
I should send the data in this format: {"file": image, "userID": userId}
This is my code in retrofit interace: #POST("avatar")
fun uploadImage(#Body data: Data ) : Call<ResponseBody>
I have created an object Data with file and userID I don't know if this is the right way. I have seen also this example
ublic interface UploadAPIs {
#Multipart
#POST("/upload")
Call<ResponseBody> uploadImage(#Part MultipartBody.Part file, #Part("name") RequestBody requestBody);
}
But I really don't understand well, how can I sen an object with part multipart
This is how I create image file that I get from gallery in my fragment
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK)
when(requestCode){
GALLERY -> {
val selectedImage= data!!.data
val file = File(selectedImage.path)
val data = Data(file, userID)
val call = api!!.uploadImage(data)
call.enqueue(object: Callback<ResponseBody>{
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Toast.makeText(context, t.message, Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
val response = response.body()!!.string()
println("Response " + response)
//response says user does not exist because the way I send data is not correct
}
})
}
CAMERA -> {
//code here
}
}
}
This is the class that I create for Data object
class Data (val file: File, userID: Int)
I would be grateful if anyone can help me :)
Updated code:
#Multipart
#POST("avatar")
fun uploadImage(
#Part("userID") user_ID: RequestBody,
#Part image: MultipartBody.Part) : Call<ResponseBody>
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK)
when(requestCode){
GALLERY -> {
val selectedImage= data!!.data
val bitmap = MediaStore.Images.Media.getBitmap(context!!.contentResolver, selectedImage)
avatar.setImageBitmap(bitmap)
val file = File(getRealPathFromURI(selectedImage))
val id = RequestBody.create(MediaType.parse("text/plain"), userID.toString())
//val reqBody = RequestBody.create(MediaType.parse(activity!!.contentResolver.getType(selectedImage)!!), file)
val reqBody = RequestBody.create(MediaType.parse("image/*"), file)
val body = MultipartBody.Part.createFormData("file", file.name, reqBody)
val call = api!!.uploadImage(id, body)
call.enqueue(object: Callback<ResponseBody>{
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Toast.makeText(context, t.message, Toast.LENGTH_SHORT).show()
println("Failure")
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful){
println("Successful " + response.body()?.string())
}else{
response.errorBody()?.string()
println("Error " + response.headers().toString())
}
}
})
}
CAMERA -> {
//code
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK)
when(requestCode){
GALLERY -> {
val selectedImage= data!!.data
val bitmap = MediaStore.Images.Media.getBitmap(context!!.contentResolver, selectedImage)
avatar.setImageBitmap(bitmap)
val file = File(getRealPathFromURI(selectedImage))
val id = RequestBody.create(MediaType.parse("text/plain"), userID.toString())
val reqBody = RequestBody.create(MediaType.parse("image/jpeg"), file)
val body = MultipartBody.Part.createFormData("file", file.name, reqBody)
val api = RetrofitClient.getInstance().apiService
val call = api!!.uploadImage(id, body)
call.enqueue(object: Callback<ResponseBody>{
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Toast.makeText(context, t.message, Toast.LENGTH_SHORT).show()
println("Failure")
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful){
println("Successful " + response.body()?.string())
}else{
response.errorBody()?.string()
println("Error " + response.headers().toString())
}
}
})
}
CAMERA -> {
//code here
}
}
}
And in API interface:
#Multipart
#POST("avatar")
fun uploadImage(
#Part("userID") user_ID: RequestBody,
/*#Part("file") name: RequestBody*/
#Part image: MultipartBody.Part) : Call<ResponseBody>
Related
i have a problem with sending files over using retrofit to my API server. When i send data over using the #Body anotation but without a file the data gets added. When i use #multipart i keep getting response.code()==401, with me using postman sending data with files through my API it works perfectly fine. The issue lies within the kotlin app and retrofit.
i used every possible way. I used multipart without file, same problem. i tried sending just the file and it was the same thing. I don't have a clue where the problem is or what it could be. using body allows me to send data over while with #multipart it looks like as if they just didn't want to transfer over. any recommendations on what to do would be very much appreciated.
myCode:
interface FarmSpaceApi {
#POST("auth/register")
fun RegisterUserApi(#Body user: User):Call<User>
#POST("auth/login")
fun LoginUserApi(#Body user: Userlogin):Call<LoginData>
//#POST("fields/create")
#Headers("Content-Type: application/json;charset=UTF-8")
#POST("fields/show")
fun getFields(#Body userId: UserId,#Header("Authorization")token:String):Call<Field2>
#Multipart
#Headers("Content-Type: application/json;charset=UTF-8")
#POST("fields/create")
fun addfield(#Header("Authorization") token:String,
#Part image:MultipartBody.Part,
#Part("fieldstype") fieldstype: RequestBody,
#Part("field_size_in_ha") field_size_in_ha: RequestBody,
#Part("harvest_in_this_year") harvest_in_this_year: RequestBody,
#Part("user_id") user_id: RequestBody):Call<FieldNewResponse>
}
clicker.setOnClickListener {
val tokenManager=TokenManager(this)
val filepath=tokenManager.getfilename()
val file= File(filepath)
Log.d("File ", file.name.toString())
Log.d("fieldname ",fieldname.toString())
Log.d("fieldsize ",fieldsize.toString())
Log.d("fieldcurrent ",fieldcurrentyear.toString())
val request=RequestBody.create(MediaType.parse("image/*"),file)
val filepart=MultipartBody.Part.createFormData("file",file.name,request)
viewModel.sendfield(
tokenManager.getTokenfromManager()
,filepart
,fieldname.toString()
,fieldsize.toString().toDouble()
,fieldcurrentyear.toString().toDouble()
,tokenManager.getidentyficator()!!.toInt())
}
#Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == 100) {
imageuri = data?.data!!
Log.d("Uri", imageuri.toString())
imagepreview.setImageURI(imageuri)
val name: String = getNameFile(this, imageuri)
Log.d("name: ", name)
val tokenManager=TokenManager(this)
tokenManager.addfilename(name)
}
}
fun getNameFile(context: Context, uri: Uri): String {
val projection: Array<String> = (arrayOf(MediaStore.Images.Media.DATA))
val cursor = context.contentResolver.query(uri, projection, null, null, null)
val index: Int = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor.moveToFirst()
val name: String = cursor.getString(index)
return name
//.substring(name.lastIndexOf("/")+1)
}
fun sendfield(token:String,filepart:MultipartBody.Part,name:String,size:Double,year:Double,id:Int) {
viewModelScope.launch {
Log.d("token ", token)
val retro = InstanceApi.getInstanceApiRetrofit().create(FarmSpaceApi::class.java)
val newname= RequestBody.create(MediaType.parse("text/plain"),name)
val newsize= RequestBody.create(MediaType.parse("text/plain"),size.toString())
val newyear= RequestBody.create(MediaType.parse("text/plain"),year.toString())
val newid= RequestBody.create(MediaType.parse("text/plain"),id.toString())
retro.addfield("Bearer " + token,filepart,newname,newsize,newyear,newid).enqueue(object : Callback<FieldNewResponse> {
override fun onResponse(call: Call<FieldNewResponse>, response: Response<FieldNewResponse>) {
Log.d("Git ", response.message().toString())
Log.d("Test ", response.body().toString())
Log.d("Testcode ", response.code().toString())
Log.d("Testerror ", response.errorBody().toString())
}
override fun onFailure(call: Call<FieldNewResponse>, t: Throwable) {
Log.d("Nie Git ", t.message.toString())
}
})
}
}
I've got the root of the problem of this implmenetation:
How to delete with Body using retrofit with usecase?
In brief:
With this usecase method like underneath I return Unit.
My logs didn't show it directly, but in debugger I have seen that the problem is because of
Service methods cannot return void.
for method NotificationApi.deleteToken
if (e.isSameExceptionAs(fromDownstream) || e.isCancellationCause(coroutineContext)) {
How to change this method deleteToken to work with delete and not return void/unit?
How to provide it with usecase properly?
Thanks.
This is my UseCase
class DeleteTokenSecondUseCase #Inject constructor(private val api: NotificationApi) :
BaseApiRequestUseCase<DeleteTokenSecondUseCase.Params, Unit>() {
override fun create(params: Params): Flow<Unit> = flow {
api.deleteToken(NotificationApi.TokenChangedBody(params.token))
}
data class Params(val token: String)
}
this is my delete
#HTTP(method = "DELETE", path = "account/firebase", hasBody = true)
fun deleteToken(#Body body: TokenChangedBody)
data class TokenChangedBody(val token: String)
fun method in vm
fun notifyNotificationTokenChanged(token: String) {
val params = DeleteTokenSecondUseCase.Params(token)
deleteTokenSecondUseCase.buildWithState(params)
.withSuccess { Log.d("build", "WORKS $params") }
.withError { Log.d("build", "NOT WORKS $params") }
.launchIn(viewModelScope)
}
EDIT:
I have implmeneted it with Call event but still it goes onFailure, guys why?
#HTTP(method = "DELETE", path = "account/firebase", hasBody = true)
fun deleteToken(#Body body: TokenChangedBody) : Call<ResponseBody>
fun notifyNotificationTokenChanged(token: String) {
val params = NotificationApi.TokenChangedBody(token)
val deleteRequest: Call<ResponseBody> = notificationApi.deleteToken(params)
deleteRequest.enqueue(object : Callback<ResponseBody?> {
override fun onResponse(call: Call<ResponseBody?>?, response: Response<ResponseBody?>?) {
Log.d("apitoken", "WORKS")
}
override fun onFailure(call: Call<ResponseBody?>?, t: Throwable?) {
Log.d("apitoken", "DOESNT WORK")
}
})
}
EDIT2:
WORKS!
SOLUTION
fun notifyNotificationTokenChanged(token: String) {
val params = NotificationApi.TokenChangedBody(token)
val deleteRequest: Call<Void> = notificationApi.deleteToken(params)
deleteRequest.enqueue(object : Callback<Void?> {
override fun onResponse(call: Call<Void?>?, response: Response<Void?>?) {
}
override fun onFailure(call: Call<Void?>?, t: Throwable?) {
}
})
}
#HTTP(method = "DELETE", path = "account/firebase", hasBody = true)
fun deleteToken(#Body body: TokenChangedBody) : Call<Void>
Someone told in Kotlin use :Completable, but haven't test it.
stackoverflow.com/questions/35429481/retrofit-2-void-return
in my project i have to send a picture to a Rest Api, am using Retrofit 2.0.9 with kotlin coroutine
the problem when i send the request i get 201 as response code that mean is Successful but the server i
cant find the picture, i find only : file name , file id .
take picture code in my fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updatePagesAppBarrTitle()
if (ActivityCompat.checkSelfPermission(
requireActivity(),
android.Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(android.Manifest.permission.CAMERA),
111
)
} else {
val i = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(i, 101)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d("sara1997", "$requestCode")
if (requestCode == 101) {
try {
val picture: Bitmap? = data?.getParcelableExtra("data")
if (picture != null) {
saveImageState(picture)
val file = convertBitmapToFile("imageName", picture)
val requestBody = file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val image =
MultipartBody.Part.createFormData("userImage", file.name, requestBody)
Picasso.get().load(file.toURL().toString()).into(Takeimageview)
_Takepicturebinding.ShowAlertDialogbtn.setOnClickListener {
takePictureViewModel.uploadPhoto(
image,
ImageUploadModel(
"page",
"ImageCreated",
"dfdd0daf-87ae-4cb3-9181-5cb1e240c3e7",
"478c60dc-329d-475b-81a3-fbe1a5a118b4",) )
} }
my repository Code
suspend fun uploadPhoto(
image: MultipartBody.Part,
imageUploadModel: ImageUploadModel
): Response<ImageUploadModel> {
val name: RequestBody =
imageUploadModel.name.toRequestBody("multipart/form-data".toMediaTypeOrNull())
val corpus: RequestBody = imageUploadModel.corpus.toString()
.toRequestBody("multipart/form-data".toMediaTypeOrNull())
val parent: RequestBody = imageUploadModel.parent.toString()
.toRequestBody("multipart/form-data".toMediaTypeOrNull())
val type: RequestBody =
imageUploadModel.type.toRequestBody("multipart/form-data".toMediaTypeOrNull())
return ApiService.APIBody.uploadPhoto(type, name, corpus, parent, image)
}
my request call code :
#Multipart
#POST("elements/create/")
suspend fun uploadPhoto(
#Part("type") type: RequestBody,
#Part("name") name: RequestBody,
#Part("corpus") corpus: RequestBody,
#Part("parent") parent: RequestBody,
#Part image: MultipartBody.Part
): Response<ImageUploadModel>
To post / put MultiPart.Parts we need to do this
Make sure the WebService / method is annotated correctly
#Multipart
#PUT("/api/profile-picture")
fun putProfilePicture(#Part imagePart: MultipartBody.Part): Single<Response<ResponseBody>>
Create the form data
val imagePart = MultipartBody.Part.createFormData(
name = "file",
filename = image.name,
body = image.asRequestBody("image/*".toMediaTypeOrNull())
)
Send the request
return webService.putProfilePicture(imagePart)
at First store your image
private fun saveToInternalStorage(bitmapImage: Bitmap, imageName: String): String? {
val cw = ContextWrapper(context)
// path to /data/data/yourapp/app_data/imageDir
val directory: File = cw.getDir("profile_image", Context.MODE_PRIVATE)
// Create imageDir
val mypath = File(directory, imageName)
var fos: FileOutputStream? = null
try {
fos = FileOutputStream(mypath)
// Use the compress method on the BitMap object to write image to the OutputStream
bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos)
} catch (e: java.lang.Exception) {
e.printStackTrace()
} finally {
try {
fos?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return directory.getAbsolutePath()}
then use OkHttpClient
saveToInternalStorage(imageView.drawable.toBitmap(), "profile.png")
val cw = ContextWrapper(context)
// path to /data/data/yourapp/app_data/imageDir
val path: File = cw.getDir("profile_image", Context.MODE_PRIVATE)
val file = File(path, "profile.png")
val requestBody =
MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("id", ID)
// Upload parameters
.addFormDataPart(
"user_image",
file?.name,
file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
) // Upload files
.build()
var request = Request.Builder().url("https://www.yoururl.com").post(requestBody).build()
var client = OkHttpClient()
client
.newCall(request)
.enqueue(
object : Callback {
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) {}
}
)
https://gist.github.com/samiullahazizi/e2f6e01253ec0a9fe998d4d4b32d67e4
I need to send the first POST request to the server, insert the received response into the second POST request and send it back to the server. How can I implement this with RxJava? At the moment, everything works, but the second question does not have time to get an answer from the first request and the imageMediaId field is sent empty.
My function:
fun uploadNewImageAndContact(toPath: String?, newContactApi: ContactsApi) {
val file = File(toPath)
val fileReqBody: RequestBody = file.asRequestBody("image/*".toMediaTypeOrNull())
val part: MultipartBody.Part =
MultipartBody.Part.createFormData("upload", file.name, fileReqBody)
// First Request
imagesService.postImage(part).enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.code() == 201) {
// Get Image response Id
val gson = Gson()
val imageResponse =
gson.fromJson(response.body()?.string(), ImageResponse::class.java)
imageMediaId = imageResponse.ids[0]
// Second Request
contactsService.postNewContact(newContactApi)
.enqueue(object : Callback<ContactsApi> {
override fun onFailure(call: Call<ContactsApi>, t: Throwable) {
}
override fun onResponse(call: Call<ContactsApi>, response: Response<ContactsApi>) {
}
})
}
}
})
}
My ViewModel function:
val newContactsApi = ContactsApi(id = "", firstName = mFirstName, lastName = mLastName, phone = mPhone,
email = mEmail, notes = mNotes, images = "https://mydb.site.io/media/${apiRepository.imageMediaId}")
apiRepository.uploadNewImageAndContact(toPath, newContactsApi)
How can I solve this problem?
class InformationActivity : AppCompatActivity() {
private val _tag = SplashActivity::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_information)
val uniqueId = SharedPreference.getidInfo(this)
val token = SharedPreference.getUserInfo(this)
Client.retrofitService.profile(uniqueId, token)
.enqueue(object : Callback<LoginResponse> {
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
}
override fun onResponse(
call: Call<LoginResponse>,
response: Response<LoginResponse>
) {
if (response?.isSuccessful == false) { val er = Gson().fromJson(response.errorBody()?.charStream(), ErrorResponse::class.java)
Log.d(_tag, "${er.code}:${er.message}")
if (er.code == 60203) {
Toast.makeText(this#InformationActivity, "", Toast.LENGTH_SHORT).show()
}
} else if (response?.isSuccessful == true) {
Glide.with(applicationContext).asBitmap().load("https://s3.amazonaws.com/appsdeveloperblog/micky.gif").into(imageView)
Toast.makeText(this#InformationActivity, "", Toast.LENGTH_LONG).show()
val file=File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"micky.gif")
var fileName="micky.gif"
val token = SharedPreference.getUserInfo(applicationContext)
val uniqueId= SharedPreference.getidInfo(applicationContext)
var requestBody: RequestBody = RequestBody.create(MediaType.parse("image/*"), file)
val body: MultipartBody.Part = MultipartBody.Part.createFormData("profile",fileName,requestBody)
if (uniqueId != null) {
Client.retrofitService.updateProfile(token,uniqueId,body)
.enqueue(object : Callback<List<LoginResponse>> {
override fun onFailure(
call: Call<List<LoginResponse>>,
t: Throwable) { Log.d("", t.message) }
override fun onResponse(
call: Call<List<LoginResponse>>,
response: Response<List<LoginResponse>>) { if (response?.isSuccessful)
{ Toast.makeText(this#InformationActivity, "File Uploaded Successfully...", Toast.LENGTH_LONG).show()
Log.d("", "" + response?.body().toString())
} else {
Toast.makeText(this#InformationActivity, "Some error occurred...", Toast.LENGTH_LONG).show()
}
} }) }
}
}
}) }
}
interface API {
#Headers("Content-Type: application/json", "Authorization:token:String")
#Multipart
#PUT("/user/profile/{userId}")
fun updateProfile(#Header("Authorization") token: String?, #Path("userId") userID: String, #Part file: MultipartBody.Part): Call<List<Loginresponse>>
}
object Client {
var retrofitService: API
init {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val logger: OkHttpClient = OkHttpClient.Builder().addInterceptor(interceptor).readTimeout(20, TimeUnit.SECONDS).writeTimeout(20, TimeUnit.SECONDS).build()
val retrofit = Retrofit.Builder()
.baseUrl("myurl")
.addConverterFactory(GsonConverterFactory.create())
.client(logger)
.build()
retrofitService = retrofit.create(API::class.java)
}
}
#SerializedName("uniqueId")
val user:String?=null
#SerializedName("nickname")
val nickname: String?=null
#SerializedName("birth")
val birth: String?=null
#SerializedName("profileImage")
val profileImage: String?=null
#SerializedName("profileThumbnail")
val profileThumbnails: String?=null
#SerializedName("gender")
val gender: Int?=null
#SerializedName("token")
val token: String? = null
}
Your json return as JSON object. But you are trying to convert into Json array
Call<List<Loginresponse>> - you try to convert result as JSON Array (list)
Solution
Get the raw json result & convert the pojo using http://www.jsonschema2pojo.org/ and try again
You are trying to store json object in list that's why you are getting error .
check your JSON response start with { curly bracket it means it is object not an array . array start with [ square bracket .
#PUT("/user/profile/{userId}")
fun updateProfile(#Header("Authorization") token: String?, #Path("userId") userID: String, #Part file: MultipartBody.Part): Call<List<Loginresponse>>
replace Call<List<Loginresponse>> with Call<Loginresponse> all over where you using updateProfile method