How to convert bitmap to file - android

I must upload image file by use retrofit
this is Api
#POST("/image")
suspend fun uploadImage(
#Body request: UploadImageRequest,
): Response<UploadImageResponse>
UploadImageRequest
data class UploadImageRequest(
#SerializedName("image") val image: String,
)
I implement get image in device storage
this is code that get image
private val imageResult = registerForActivityResult(
StartActivityForResult(),
) { result ->
if (result.resultCode == RESULT_OK) {
val imageUri = result.data?.data
imageUri?.let {
if (Build.VERSION.SDK_INT < 28) {
bitmap =
MediaStore.Images.Media.getBitmap(this.contentResolver, imageUri)
} else {
val source = ImageDecoder.createSource(this.contentResolver, imageUri)
bitmap = ImageDecoder.decodeBitmap(source)
}
}
binding.imageActivityChangeUserInformationUserProfile.setImageBitmap(bitmap)
}
}
I don't know how I can upload that image
I tried that image to string by use base64 encode
but encoding string is too long
I tried create temp image file in device storage.
But, changed policy, scoped storage?
I couldn't make a new file
my app sdk is 33

If you have a file and you want to upload that file then do not first make a bitmap out if it.
Certainly not if you then ask to save that bitmap to file as then you can start over again.
And pdf's and doc's let them convert to bitmap pretty bad.
So just upload the file.
Just the bytes of the file.

Related

How to upload an image to storage firebase from an url

When I get the ImageURI from the edamam API and try to upload it it gives some errors, which I am not getting when I transform the BitMap I get when taking a picture and transform it into URI, I have tried to transform the ImageURI into Bitmap to pass it to the same function but that doesnt work. This below is the function that does work
fun bitmapToUri(imageBitmap: Bitmap, cacheDir: File): Uri {
val baos = ByteArrayOutputStream()
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
data2 = baos.toByteArray()
val file = File(cacheDir, imageBitmap.toString())
// When calling it with imageBitmap.toString() each item has its image (before the same one repeated)
file.delete()
// Just in case there is another File
file.createNewFile()
val fileOS = FileOutputStream(file)
fileOS.write(data2)
fileOS.flush()
fileOS.close()
baos.close()
return file.toUri()
}
I am getting these errors
E/UploadTask: could not locate file for uploading:
https://www.edamam.com/food-img/28a/28ae0e32feff919253b2cd17c47d2f23.jpg
E/StorageException: StorageException has occurred.
An unknown error occurred, please check the HTTP result code and inner exception for server response.
Code: -13000 HttpResult: 0
E/StorageException: No content provider: https://www.edamam.com/food-img/28a/28ae0e32feff919253b2cd17c47d2f23.jpg
java.io.FileNotFoundException: No content provider: https://www.edamam.com/food-img/28a/28ae0e32feff919253b2cd17c47d2f23.jpg
This is the function I have to upload to Storage
private fun uploadImage() {
val name = binding.inputName?.editText?.text.toString()
val provider = binding.inputProvider?.editText?.text.toString()
val address = binding.inputStreet?.editText?.text.toString()
val fileName = "$name $provider $address"
Log.d("INFO IMAGE URI UPLOAD->>", imageUri.toString())
storage = FirebaseStorage.getInstance().getReference("images/$fileName")
storage.putFile(imageUri).addOnSuccessListener {
binding.imageButton.setImageURI(null)
}.addOnFailureListener {
Toast.makeText(this#NewItem, "Image Not Uploaded", Toast.LENGTH_SHORT).show()
}
}
And this is the value I'm passing to imageUri
imageUri = apiCallBody.listHints.first().food.image.toUri()
Which is this value
https://www.edamam.com/food-img/862/862434152a3191f30c889f10eb2989b0.jpg
The Firebase Storage SDK doesn't allow you to directly upload the contents of random http URLs like the one you're showing here. The putFile method requires a Uri from a ContentProvider that's serviced by an Android app on the device. That's not what you're doing here at all.
If you want to upload an image that exists outside your app, you will have to download it first locally, then upload the data that you downloaded using putBytes, putStream or putFile.
My Method of setting a selected Image to a image view to then be Uploaded is
private val ImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()){ uri: Uri? ->
uri?.let { binding.GroupImageAdd.setImageURI(uri)
ImageUri = uri
}
}
The Uri is a Local val
set when the images is selected from file
and then
fun UploadImage(UUid: String, uri: Uri, ImageType: String) {
val storageRef = FirebaseStorage.getInstance().reference
Log.d("URI", uri.toString())
val task = storageRef.child("**Image Directory").putFile(uri)
task.addOnSuccessListener {
Log.d("UploadImage", "Task Is Successful")
}.addOnFailureListener {
Log.d("UploadImageFail", "Image Upload Failed ${it.printStackTrace()}")
}
}
Unsure if this helps

How to convert content uri to file?

I want to send multiple images to my database using retrofit. I am using this code to select multiple images.
private val galleryLauncher =
registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { list ->
//TODO convert all content uris to File
}
I have tried a bunch of image picker libraries but none of them works in my device (Android R).
How do I convert them to file? Please help.
Is there any other method to send images to server via Retrofit2?
I used this dependency
implementation "commons-io:commons-io:2.7"
And this method
private fun createFileFromUri(name: String, uri: Uri): File? {
return try {
val stream = context.contentResolver.openInputStream(uri)
val file =
File.createTempFile(
"${name}_${System.currentTimeMillis()}",
".png",
context.cacheDir
)
FileUtils.copyInputStreamToFile(stream, file) // Use this one import org.apache.commons.io.FileUtils
file
} catch (e: Exception) {
e.printStackTrace()
null
}
}
private val galleryLauncher =
registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { list ->
//TODO convert all content uris to File
}

How do I keep an image saved on an application even after the app closes?

I'm making a card holder app, the user is supposed to take a picture of his id, contacts, notes etc. so he can later use them digitally. Problem is how do I take a camera input and save it as an image inside the application so it stays there?
You can simply use the native Camera Application of your device to get the image and then save it to the device . Android Team has done much easy for developers to perform such task.
You need to make use of ActivityContracts and MediaStore to take the image and store it into your device respectively.
Step 1 :
First Generate a Uri for your Image , in the following manner
#RequiresApi(Build.VERSION_CODES.Q)
suspend fun createPhotoUri(source: Source): Uri? {
val imageCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val dirDest = File(
Environment.DIRECTORY_PICTURES,
context.getString(R.string.app_name) + File.separator + "CAMERA"
)
val date = System.currentTimeMillis()
val fileName = "$date.jpg"
return withContext(Dispatchers.IO) {
val newImage = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.RELATIVE_PATH, "$dirDest${File.separator}")
}
return#withContext context.contentResolver.insert(imageCollection, newImage)
}
}
Step 2:
Then when you want to capture the Image , then on the OnClickListener perform the following :
binding.takePictureButton.setOnClickListener {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.createPhotoUri(Source.CAMERA)?.let { uri ->
actionTakePicture.launch(uri)
}
}
}
Step 3 :
The actionTakePicture ActivityContract is as follows :
private val actionTakePicture = registerForActivityResult(TakePicture()) { success ->
if (!success) {
Log.d(tag, "Image taken FAIL")
return#registerForActivityResult
}
Log.d(tag, "Image taken SUCCESS")
}
And you are done with capturing you Image and storing it .
Make sure you declare permission's before using the above code ,else it wont work .
The answer mentioned by #StefanoSansone can also be used . But the issue with that is you need to perfectly setup CameraX library and that might be tedious for your useCase . One should use library like CameraX if they want to have more control on Camera with other camera capabilities , when you application is more of a Camera Application . Else using the above method is perfectly fine . Saves one from tedious work .
If you are using Kotlin and Jetpack libraries, I suggest you to take a look to CameraX library.
You can use the takePicture method to take a photo with camera and save it in the storage.
A complete example can be found in the CameraX codelab
private fun takePhoto() {
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.US
).format(System.currentTimeMillis()) + ".jpg")
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Set up image capture listener, which is triggered after photo has
// been taken
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
I had some difficulties last week with saving images.
I finally used sharedPreferences files and saved bitmap as text.
From what I've heard it's not a good practice, It's better to save in files and save the path.
However the code is very compact and it's working really well (in my case never have to load more than 4 pictures)
var bm= MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImageUri)
bm.compress(Bitmap.CompressFormat.PNG, 100, baos) //bm is the bitmap object
val b = baos.toByteArray()
val encoded: String = Base64.encodeToString(b, Base64.DEFAULT)
editor.putString("backgroundBitmap",encoded)//put the bitmap as text in sharedPref files, use to back the bitmap in mainActivity
editor.commit()

Kotlin Image Compression Implementation

My old implementation to upload image to Firebase Storage in JPEG format without any compression
private fun sendToFirebase() {
if (imgUri != null) {
val fileRef = storageRef!!.child(username+ ".jpg")
....
// code to upload and read image url
}
}
Decided to write a image compression technique to compress image and then upload to Firebase Storage
Result : Achieved image compression technique, see below
Newly added code to compress image
URI to Bitmap
val bitmap = MediaStore.Images.Media.getBitmap(activity?.contentResolver, imgUri)
Method to compress Bitmap
private fun compressBitmap(bitmap: Bitmap, quality:Int):Bitmap{
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.WEBP, quality, stream)
val byteArray = stream.toByteArray()
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
}
Bitmap compression Implementation
compressBitmap(bitmap, 80)
Query: How to upload same compressed image to Firebase storage
private fun sendToFirebase() {
if (imgUri != null) {
// code to convert uri to bitmap <start>
val bitmap = MediaStore.Images.Media.getBitmap(activity?.contentResolver, imgUri)
compressBitmap(bitmap, 80)
// code to convert uri to bitmap <end>
// old implementation
.....
}
}
You don't seem to be passing anything into your function for sendtoFirebase. i am posting code i have done to successfully upload.
you looking at compressing first so you would need this;
private fun compressBitmap(bitmap: Bitmap, quality: Int): Bitmap {
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.WEBP,quality,stream)
val byteArray = stream.toByteArray()
arrayByte = byteArray
uploadFile(arrayByte)
return BitmapFactory.decodeByteArray(byteArray,0,byteArray.size)
}
in the above, uploadFile is the call for the firebase upload. i am passing the compressed bitmap into the function. the functional for upload looks as follows:
in below mImageURI is a companion object which is part of the URI passed for compression. you can remove the if statement below if you dont want to do the check
private fun uploadFile(data:ByteArray) {
if (mImageUri != null){
val storageref = imageref.child("put your image id here")
storageref.putBytes(data).addOnSuccessListener {
Handler().postDelayed({
progressbar.setProgress(0)
Toast.makeText(activity, "Upload Successful", Toast.LENGTH_LONG).show()
}
, 1000)
}.addOnFailureListener{e->
Toast.makeText(activity,e.message,Toast.LENGTH_LONG).show()
}.addOnProgressListener {taskSnapshot ->
val progress = (100.0 * taskSnapshot.bytesTransferred/taskSnapshot.totalByteCount)
progressbar.setProgress(progress.toInt())
}
}
else if(mImageUri == null) {
Toast.makeText(activity,"No File Selected",Toast.LENGTH_LONG).show()
}
}
You do not need to have the progress bar above. its just a nice visual for the user to have to see the progress of the upload if the file is large.
your really only need to ensure that you passing data into .putbytes
Edit: For your onActivity result if your code is similar to mine then use;
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK
&& data != null && data.getData() != null) {
mImageUri = data.getData()!!
image1.setImageURI(data.getData())
}
}
in the above image1 is a imageView on the current page to show the image selected.
Hope this helps

Kotlin: How to convert an image Uri to Bitmap to Base64

I've found lots of answers related tothis in Java, supposedely a Kotlin solution would be very much like Java, but as in many other things, the devil is on the details, and there are some.
I have several Uris stores in the SQLite database I'm using, now I want to send this images to an API that will catch them along with other data. I'll send the info via POST.
So now, I want to load the Uri, as I do when I use an ImageView.setImageURI() that will take the Uri, convert to Bitmap and place it in the ImageView container.
How do I convert from that Uri to a Bitmap object, and then encode it to Base64 to send it to the API, using Kotlin code?
EDIT
I'm trying with Anupam's imageFileToBase64() which seems to be exactly what I want, now I'm having a problem, I got a FileNotFoundException. This is what I'm doing.
I recover the Uri string from the database, it is a string that reads: content://media/external/images/media/109, so I convert it to an Uri
val uri = Uri.parse(uri_string)
Then I get the real path, and convert it to File
val file = File(uri.path)
Finally I call the function
val base64 = imageFileToBase64(file)
I have tried both with uri.path and uri.toString() and got the same results.
uri.path = /external/images/media/109
uri.toString() = content:/media/external/images/media/109
So I got no idea on what to pass to the function.
These are Kotlin methods for the following -
1. Get the bitmap from assets
2. Save bitmap to a file
3. Get Base64 from bitmap
4. Encode File/Image to Base64
5. Decode Base64 to File/Image
// Get the bitmap from assets and display into image view
val bitmap = assetsToBitmap("tulip.jpg")
// If bitmap is not null
bitmap?.let {
image_view_bitmap.setImageBitmap(bitmap)
}
val imagePath = "C:\\base64\\image.png"
// Encode File/Image to Base64
val base64ImageString = encoder(imagePath)
println("Base64ImageString = $base64ImageString")
// Decoder Base4 to File/Image
decoder(base64ImageString, "C:\\base64\\decoderImage.png")
// Click listener for button widget
button.setOnClickListener{
if(bitmap!=null){
// Save the bitmap to a file and display it into image view
val uri = bitmapToFile(bitmap)
image_view_file.setImageURI(uri)
// Show a toast message
toast("Bitmap saved in a file.")
}else{
toast("bitmap not found.")
}
}
}
// Method to get a bitmap from assets
private fun assetsToBitmap(fileName:String):Bitmap?{
return try{
val stream = assets.open(fileName)
BitmapFactory.decodeStream(stream)
}catch (e:IOException){
e.printStackTrace()
null
}
}
// Method to save an bitmap to a file
private fun bitmapToFile(bitmap:Bitmap): Uri {
// Get the context wrapper
val wrapper = ContextWrapper(applicationContext)
// Initialize a new file instance to save bitmap object
var file = wrapper.getDir("Images",Context.MODE_PRIVATE)
file = File(file,"${UUID.randomUUID()}.jpg")
try{
// Compress the bitmap and save in jpg format
val stream:OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG,100,stream)
stream.flush()
stream.close()
}catch (e:IOException){
e.printStackTrace()
}
// Return the saved bitmap uri
return Uri.parse(file.absolutePath)
}
// Method to get Base64 from bitmap
private fun imageFileToBase64(imageFile: File): String {
return FileInputStream(imageFile).use { inputStream ->
ByteArrayOutputStream().use { outputStream ->
Base64OutputStream(outputStream, Base64.DEFAULT).use { base64FilterStream ->
inputStream.copyTo(base64FilterStream)
base64FilterStream.flush()
outputStream.toString()
}
}
}
}
// Encode File/Image to Base64
private fun encoder(filePath: String): String{
val bytes = File(filePath).readBytes()
val base64 = Base64.getEncoder().encodeToString(bytes)
return base64
}
// Decode Base64 to File/Image
private fun decoder(base64Str: String, pathFile: String): Unit{
val imageByteArray = Base64.getDecoder().decode(base64Str)
File(pathFile).writeBytes(imageByteArray)
}

Categories

Resources