Error uploading Multipart to MongoDB using Ktor - android

I'm trying to upload a JPEG to MongoDB using Ktor and Kotlin but keep getting an error:
java.io.FileNotFoundException: /document/image:32: open failed: ENOENT (No such file or directory)
Please help if someone knows what I am doing wrong here
It is a strange error, because I'm sure that uri is ok. I can display an image in app with this.
First things first:
KTOR SIDE:
fun Route.uploadFile(
app: Application){
val client = KMongo.createClient()
val database = client.getDatabase(IMAGES_DATABASE_NAME)
val bucket = GridFSBuckets.create(database, "images")
post("/upload_photo") {
try {
val multipartData = call.receiveMultipart()
multipartData.forEachPart { part ->
if (part is PartData.FileItem) {
val fileName = part.originalFileName as String
withContext(Dispatchers.IO) {
bucket.uploadFromStream(fileName, part.streamProvider())
}
call.respond(
message = ApiResponse(
success = true,
message = ""
)
)
} else {
call.respond(
message = ApiResponse(
success = false,
message = ""
)
)
}
}
} catch (e: Exception) {
app.log.info("Error: ${e.message}")
call.respond(
message = ApiResponse(
message = "Error: ${e.message}",
success = false
),
status = HttpStatusCode.BadRequest
)
}
}
API INTERFACE:
#Multipart
#POST("/upload_photo")
suspend fun uploadPhoto(
#Part filePart: MultipartBody.Part
):ApiResponse
Now, I suppose that the problem occures on kotlin Android App side:
Function where the problem occures:
override suspend fun uploadFile(photo: Uri): ApiResponse {
return try {
val file = photo.path?.let { File(it) }
val requestBody = file!!.asRequestBody()
val body = MultipartBody.Part.createFormData("photo",file.name, requestBody )
Log.d("Upload", body.toString())
ktorApi.uploadPhoto(body)
} catch (e: Exception) {
Log.d("Upload: ", "{ $e.message }")
ApiResponse(
success = false,
error = e
)
}
}

I found a solution to my problem.
To make a post with Uri possible is it necessary to create a temporary file in local cashe. For example with the method like thise:
private fun createFileFromUri(name: String, uri: Uri, context: Context): File? {
return try {
val stream = context.contentResolver.openInputStream(uri)
val file =
File.createTempFile(
"${name}_${System.currentTimeMillis()}",
".jpg",
context.cacheDir
)
FileUtils.copyInputStreamToFile(stream, file) // Use this one import org.apache.commons.io.FileUtils
file
} catch (e: Exception) {
e.printStackTrace()
null
}
}
and a dependency in gradle :
implementation group: 'commons-io', name: 'commons-io', version: '2.7'
implementation group: 'org.apache.commons', name: 'commons-text', version: '1.9'

Related

How to pass pdf form-data body with Retrofit2

I want to upload a pdf file with other fields using Retrofit2. But I don't know how to create a request body for it.
#POST("filebase/upload")
suspend fun uploadPdfDocument(#Header(HEADER_AUTHORIZATION) authorization: String, #Body requestBody: RequestBody): UploadPdfDocumetResponse?
Please help me!
If in your dependencies exists okhttp3 then you can use this beautiful extenstion function
import okhttp3.RequestBody.Companion.asRequestBody
yourPdfFile.asRequestBody(yourPdfFile.getExtension())
getExtension() function below
fun File.getExtension(): MediaType? {
var type: String? = null
val encoded: String = try {
URLEncoder.encode(name, "UTF-8").replace("+", "%20")
} catch (e: Exception) {
name
}
val extension = MimeTypeMap.getFileExtensionFromUrl(encoded)
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
}
return type?.toMediaType()
}

Upload Image to Server using Multipart and Request Body in Android in Kotlin

I am stuck in between a strange issue of uploading image file to server. Although I did upload file several times before, but this time I don't understand what is the issue.
I get the file path of respective file but RequestBody returns null. Below I mentioned what library I'm using.
I am using kotlin, MultiPart and RequestBody for file upload.
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
Below is my code which I wrote for file upload. In which you can see GalleryCameraUtility.getImageRequestBody(imageFile) returns null for file.
File path from mobile device /storage/emulated/0/DCIM/Screenshots/test_image.jpg
fun addNewCompany(companyName: String, email: String,imageFile: File, ownerName: String, address: String, companyDetails: String){
val companyNameBody: RequestBody = companyName.toRequestBody("text/plain".toMediaType())
val emailBody: RequestBody = email.toRequestBody("text/plain".toMediaType())
val fileData: RequestBody? = GalleryCameraUtility.getImageRequestBody(imageFile)
val ownerNameBody: RequestBody = ownerName.toRequestBody("text/plain".toMediaType())
val addressBody: RequestBody = address.toRequestBody("text/plain".toMediaType())
val userIdBody: RequestBody = PreferenceHelper.readUserIdPref(Constants.USER_ID).toString()
.toRequestBody("text/plain".toMediaType())
addCompanyRepo.addNewCompanyApi(companyNameBody, emailBody, fileData, ownerNameBody, addressBody, userIdBody)
}
class GalleryCameraUtility {
companion object{
fun getImageRequestBody(sourceFile: File) : RequestBody? {
var requestBody: RequestBody? = null
Thread {
val mimeType = getMimeType(sourceFile);
if (mimeType == null) {
Log.e("file error", "Not able to get mime type")
return#Thread
}
try {
requestBody = sourceFile.path.toRequestBody("multipart/form-data".toMediaTypeOrNull())
/*MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart(serverImageKey, uploadedFileName,sourceFile.asRequestBody(mimeType.toMediaTypeOrNull()))
.build()*/
} catch (ex: Exception) {
ex.printStackTrace()
Log.e("File upload", "failed")
}
}.start()
return requestBody;
}
// url = file path or whatever suitable URL you want.
private fun getMimeType(file: File): String? {
var type: String? = null
val extension = MimeTypeMap.getFileExtensionFromUrl(file.path)
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
}
return type
}
}
}
I spent so many hours on this but not able to find solution. Please help me out on this.

using gmail api getting exception no object DCH for MIME type multipart/mixed >> , nothing solved my issue

I'm trying to send email with attachment by using gmail api. I coppied and added some changes to my code and try to send it.
Although sending text email was successful, sending mail with attachment is failed. The exception is triggered after writing from email to buffer.
no object DCH for MIME type multipart/mixed
here is my code
fun sendMail(
c: Context,
ac: Account,
from: String,
tos: List<String>,
subject: String,
content: String,
vararg f: File
) {
val props = Properties()
val session = Session.getDefaultInstance(props, null)
var email = MimeMessage(session)
try {
email.setFrom(InternetAddress(from));
for (to in tos) {
email.addRecipient(
javax.mail.Message.RecipientType.TO,
InternetAddress(to)
);
}
email.setSubject(subject);
var mimeBodyPart = MimeBodyPart();
mimeBodyPart.setContent(content, "text/plain");
val multipart = MimeMultipart()
multipart.addBodyPart(mimeBodyPart);
for (file in f) {
mimeBodyPart = MimeBodyPart();
var source: DataSource = FileDataSource(file)
mimeBodyPart.dataHandler = DataHandler(source)
mimeBodyPart.fileName = file.name;
}
multipart.addBodyPart(mimeBodyPart);
email.setContent(multipart);
var message = createMessageWithEmail(email)
val gmailService = getService(c, ac)
val returnedMessage = gmailService?.users()?.messages()?.send("me", message)?.execute()
message.id = returnedMessage?.id
message.labelIds = returnedMessage?.labelIds
println("returnedMessage : $returnedMessage");
println("currentMessage : " + message.toPrettyString())
} catch (e: MessagingException) {
Log.e(TAG, "sendMail: $e")
} catch (e: IOException) {
Log.e(TAG, "sendMail: $e")
}
}
fun createMessageWithEmail(emailContent: MimeMessage): Message {
val message = Message()
try {
val buffer = ByteArrayOutputStream()
emailContent.writeTo(buffer)
val bytes = buffer.toByteArray()
val encodedEmail = encodeBase64URLSafeString(bytes)
message.raw = encodedEmail
} catch (e: MessagingException) {
Log.e(TAG, "createMessageWithEmail: $e")
} catch (e: IOException) {
Log.e(TAG, "createMessageWithEmail: $e")
}
return message
}
I have tried to add Thread.currentThread().setContextClassLoader(javax.mail.Message::class.java.getClassLoader())
but it didnt solved.
I'm so confused about it. How does not the code which is in the documentation work? I don't understand what I'm missing.
I'm getting file from internal storage. I don't think it is about it. Can you help me what I'm missing?
Thank you !

Trying to upload image from Android to AWS Lambda

I am using this code in Kotlin/Android to upload image to server:
fun uploadImageWithLambda(){
val file = File(filePath)
val retrofit = NetworkClient.getRetrofitObj()
val uploadApis = retrofit.create(UploadApis::class.java)
val requestBody = RequestBody.create(MediaType.parse("image/*"), file)
val part = MultipartBody.Part.createFormData("image", file.name, requestBody)
val call = uploadApis.uploadImage(imageRequest = part)
call.enqueue(object : Callback<ImageResponse> {
override fun onFailure(call: Call<ImageResponse>, t: Throwable) {
Log.e(TAG, t.toString())
}
override fun onResponse(call: Call<ImageResponse>, response: Response<ImageResponse>) {
Log.i(TAG, "success")
}
})
}
This is function in UploadApis
#Multipart
#POST("prod/res")
fun uploadImage(#Part imageRequest: MultipartBody.Part) : Call<ImageResponse>
The file that I received in Lambda cannot be open. When I removed some bytes from that file I got proper image file.
First lines of code in my lambda function:
const multipart = require('aws-multipart-parser')
exports.handler = async (event) => {
try {
CONSTANTS.logger.info("POST image in S3...")
let spotText = {}
const result = multipart.parse(event, spotText)
fs.writeFileSync('original.jpg', result.image.content)
const content = result.image.content;
console.log(result)
var newBuffer = new Buffer(content.length - 22);
content.copy(newBuffer, 0, 22, content.length);
// const sliced = content.slice(25);
console.log(newBuffer);
fs.writeFileSync('new.jpg', content);
....
I also made a small express application with one route to upload the image to S3 and I sent the image from Android in the same way and it worked, I could open the image.
Is there anyone having the same/similar issue?

I can't find the files I uploaded to Google Drive

I'm trying to upload a file to Google Drive using Google Drive REST API v3. After the upload process is completed, it returns a status code of 200 (successful). But I can't find the files in my Google Drive. Please tell me what am I doing wrong? I will really appreciate if you provide a proper illustration or better still code snippet while helping me with this problem of mine. I am really anticipating your answers.
I have tried following the documentation but I am still getting the same error. I have searched everywhere online and stackoverflow, but none seems to provide the solution to my problem.
here is the code
private val AUTHORIZATION_PARAM = "Authorization"
private val BEARER_VAL = "Bearer "
private val CONTENT_TYPE_PARAM = "Content-Type: "
private val LINE_FEED = "\r\n"
private val APP_FOLDER_ID = "appDataFolder"
fun connectAndStartOperation() {
if (mAuthCode == null) {
signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestProfile()
.requestScopes(Scope(Scopes.DRIVE_APPFOLDER))
.requestIdToken(resources.getString(R.string.gdrive_clientId))
.requestServerAuthCode(resources.getString(R.string.gdrive_clientId))
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, signInOptions!!)
startActivityForResult(mGoogleSignInClient?.signInIntent, CLOUD_STORAGE)
Log.i("mAuthCode", "false")
} else {
Log.i("mAuthCode", "true")
writeDbToDrive()
mNextGoogleApiOperation = INVALID;
}
}
fun disconnect() {
mGoogleSignInClient?.signOut()
mActivity = null
mNextGoogleApiOperation = INVALID
mAuthCode = null
mAccessToken = null
mTokenExpired = 0
}
override fun onDestroy() {
disconnect()
super.onDestroy()
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CLOUD_STORAGE) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
.addOnSuccessListener(this)
.addOnFailureListener(this)
}
}
override fun onSuccess(googleSignInAccount: GoogleSignInAccount?) {
Log.i("mAuthCode", "Success")
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
idTokenString = googleSignInAccount?.idToken
mAuthCode = googleSignInAccount?.serverAuthCode
mGoogleSignInAccount = googleSignInAccount
doAsync {
try {
mAccessToken = GoogleAuthUtil.getToken(this#SettingsActivity, mGoogleSignInAccount?.account, scope)
} catch (e: Exception) {
Log.i("Error AccessToken", "${e.message}")
e.printStackTrace()
}
uiThread {
Log.i("AccessTokenMy", "$mAccessToken")
}
}
}
override fun onFailure(p0: java.lang.Exception) {
Log.i("mAuthCode", "Failed")
p0.printStackTrace()
}
private fun writeDbToDrive() {
var conn: HttpURLConnection? = null
var os: OutputStream? = null
val accessToken = requestAccessToken(mGoogleSignInAccount!!)
if (accessToken == null)
return
try {
val boundary = "pb" + System.currentTimeMillis()
val url = URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart")
conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.useCaches = false
conn.doOutput = true
conn.doInput = true
conn.connectTimeout = 5000
conn.setRequestProperty(AUTHORIZATION_PARAM, BEARER_VAL + accessToken!!)
conn.setRequestProperty("Content-Type", "multipart/related; boundary=$boundary")
Log.i("Action", "Parameter set for server")
/////// Prepare data
//val timestamp = SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.getDefault()).format(Date())
// Prepare file metadata (Change your backup file name here)
val b = StringBuilder()
b.append('{')
.append("\"name\":\"").append(exportedFileName).append('\"')
.append(',')
.append("\"mimeType\":").append("\"text\\/csv\"")
.append(',') //"\"application\\/vnd.google-apps.unknown\""
.append("\"parents\":").append("[\"").append(APP_FOLDER_ID).append("\"]")
.append('}')
val metadata = b.toString()
val data = readFile(File(filePath))
/////// Calculate body length
var bodyLength = 0
// MetaData part
b.setLength(0)
b.append("--").append(boundary).append(LINE_FEED)
b.append(CONTENT_TYPE_PARAM).append("application/json; charset=UTF-8")
.append(LINE_FEED)
b.append(LINE_FEED)
b.append(metadata).append(LINE_FEED)
b.append(LINE_FEED)
b.append("--").append(boundary).append(LINE_FEED)
b.append(CONTENT_TYPE_PARAM).append("text/csv").append(LINE_FEED)
b.append(LINE_FEED)
val beforeFilePart = b.toString().toByteArray(charset("UTF_8"))
bodyLength += beforeFilePart.size
bodyLength += data.size // File
b.setLength(0)
b.append(LINE_FEED)
b.append("--").append(boundary).append("--")
val afterFilePart = b.toString().toByteArray(charset("UTF_8"))
bodyLength += afterFilePart.size
conn.setRequestProperty("Content-Length", bodyLength.toString())
//if (BuildConfig.DEBUG_MODE) DebugHelper.log("LENGTH", bodyLength)
/////// Write to socket
os = conn.outputStream
try {
os!!.write(beforeFilePart)
os!!.write(data)
os!!.write(afterFilePart)
os!!.flush()
} catch (e: Exception) {
e.printStackTrace()
}
val msg = conn.responseMessage
val code = conn.responseCode
if (code == 200) {
Log.i("writeDbToDrive", "Exported Successfully: $code $msg")
} else {
Log.i("writeDbToDrive", "Error: $code $msg")
}
} catch (e: Exception) {
e.printStackTrace()
Log.i("writeDbToDrive", e.message!!)
} finally {
if (os != null) {
try {
os!!.close()
} catch (e: IOException) {
}
}
conn?.disconnect()
}
}
#Throws(IOException::class)
private fun readFile(file: File): ByteArray {
val f = RandomAccessFile(file, "r")
try {
val longlength = f.length()
val length = longlength.toInt()
if (length.toLong() != longlength)
throw IOException("File size >= 10 Mb")
val data = ByteArray(length)
f.readFully(data)
return data
} finally {
f.close()
}
}
private fun requestAccessToken(mGoogleSignInAccount: GoogleSignInAccount): String? {
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
doAsync {
try {
mAccessToken = GoogleAuthUtil.getToken(this#SettingsActivity, mGoogleSignInAccount?.account, scope)
} catch (e: Exception) {
Log.i("Error AccessToken", "${e.message}")
e.printStackTrace()
}
uiThread {
Log.i("AccessTokenMy", "$mAccessToken")
}
}
return mAccessToken
}
After reading through this Files: create Documentation, I have finally fixed the problem. Unknown to me is that the files where being saved in the AppData folder created by my app. The AppData folder is hidden which can only be accessible by and through my app. For me to be able to save the file to My Drive folder, I removed the part of the metadata
` .append(',')
.append("\"parents\":").append("[\"").append(APP_FOLDER_ID).append("\"]")`
So the metadata part is now like this
val b = StringBuilder()
b.append('{')
.append("\"name\":\"").append(exportedFileName).append('\"')
.append(',')
.append("\"mimeType\":").append("\"text\\/csv\"")
.append('}')
val metadata = b.toString()
Every other thing remains the same

Categories

Resources