Blob Row too big too fit in Android - java

How can I limit the size of the image when saving to SQLite? I have this error when I retrieve which I think didn't get the big size of the blob Image from SQLite. However, I tried putting limit 500 in the query like this SELECT id,cash_card,hh_number,cc_image FROM CgList limit 500, but the result is the same, it crashes my application.
In short, Is there any way to reduce the file size of the image when inserting to the SQLite database?
Error
android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit
into CursorWindow requiredPos=0, totalRows=4
Insert data and blob
public void insertData(String cash_card, String hh_number,String series_number ,byte[] cc_image,byte[] id_image){
SQLiteDatabase database = getWritableDatabase();
String sql = "INSERT INTO CgList VALUES (NULL,?, ?, ?, ?, ?)";
SQLiteStatement statement = database.compileStatement(sql);
statement.clearBindings();
statement.bindString(1, cash_card);
statement.bindString(2, hh_number);
statement.bindString(3, series_number);
statement.bindBlob(4, cc_image);
statement.bindBlob(5, id_image);
statement.executeInsert();
}
Getting data
Cursor cursor = ScannedDetails.sqLiteHelper.getData("SELECT id,cash_card,hh_number,cc_image FROM CgList");
list.clear();
while (cursor.moveToNext()) { // the error is here
int id = cursor.getInt(0);
String name = cursor.getString(1);
String price = cursor.getString(2);
byte[] image = cursor.getBlob(3);
list.add(new Inventory(name, price, image, id));
}
adapter.notifyDataSetChanged();
When I click button to save to SQLite
btnSubmit.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
sqLiteHelper.insertData(
edtCashCard.getText().toString().trim(),
edtHhnumber.getText().toString().trim(),
edtSeriesno.getText().toString().trim(),
imageViewToByte(mPreviewCashcard),
imageViewToByte(mPreview4PsId)
);
}
});
Converting Image To Byte
public static byte[] imageViewToByte(ImageView image) {
Bitmap bitmap = ((BitmapDrawable)image.getDrawable()).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
return byteArray;
}
Updated
I think this is the problem when capturing an image it provide bigger size, I need this because after I capture the image I want to crop the image for some purpose but I want to display the actual capture not the cropped image to another activity
private void pickCamera() {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "NewPic");
values.put(MediaStore.Images.Media.DESCRIPTION, "Image to Text");
image_uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);
Intent cameraIntent = new Intent (MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri);
startActivityForResult(cameraIntent, IMAGE_PICK_CAMERA_CODE);
}
OnActivityResult
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK){
if (requestCode == IMAGE_PICK_GALLER_CODE){
CropImage.activity(data.getData()).setGuidelines(CropImageView.Guidelines.ON).start(this);
}
if (requestCode == IMAGE_PICK_CAMERA_CODE){
CropImage.activity(image_uri).setGuidelines(CropImageView.Guidelines.ON).start(this);
}
}
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE){
CropImage.ActivityResult result = CropImage.getActivityResult(data);
if(resultCode ==RESULT_OK){
Uri resultUri = result.getUri();
resultUri.getPath();
mPreviewIv.setImageURI(resultUri);
BitmapDrawable bitmapDrawable = (BitmapDrawable)mPreviewIv.getDrawable();
Bitmap bitmap = bitmapDrawable.getBitmap();
TextRecognizer recognizer = new TextRecognizer.Builder(getApplicationContext()).build();
if(!recognizer.isOperational()){
Toast.makeText(this,"Error",Toast.LENGTH_SHORT).show();
}
else{
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
SparseArray<TextBlock> items = recognizer.detect(frame);
StringBuilder sb = new StringBuilder();
for (int i = 0; i<items.size(); i++){
TextBlock myItem = items.valueAt(i);
sb.append(myItem.getValue());
sb.append("\n");
}
Intent i = new Intent(MainActivity.this, ScannedDetails.class);
//camera
i.putExtra("CashCardImage",image_uri.toString()); //This data pass to another activity
startActivity(i);
}
}
}
}
Retrieve to another Activity
Bundle extras = getIntent().getExtras();
String resultUri = extras.getString("CashCardImage");
Uri myUri = Uri.parse(resultUri);
mPreviewCashCard.setImageURI(myUri);
The other one I tried when saving to SQLITE the size is not big it's just KIB, I think the problem is on the 1st PickCamera but those code need for cropping an Image
private void pickCamera() {
Intent intent = new Intent(ScannedDetails.this, InventoryList.class);
startActivity(intent);
}

However, I tried putting limit 500 in the query like this SELECT id,cash_card,hh_number,cc_image FROM CgList limit 500
LIMIT limits the number of rows returned, is does not limit the size of a row.
The issue is that a single row exceeds the capacity of the cache/buffer (CursorWindow) that a Cursor uses. This restriction does not apply when inserting the said row/rows.
The typical solution is to not store images but to instead store a reference, such as a file path, from which the image (or other large item) can then be retrieved.
Here's an example that stores images less than a set size (100k in the example) as a blob and those larger as files in data/data/myimages, which could be another solution.
It is possible to break a large image into chunks of data and store/retrieve that. Here's an example of doing that.

Don't store an image directly in your database. Instead, save the image to a file or to a webserver. Then in the database store the file path or URL to the image.

Related

Adding an Image to a sqlite database

I've been struggling for a while now with this, and i want to be able to store an image to my database. I know storing an image to a database directly is bad practice but i simply do not know enough to be able to do it another way.
So, i'm currently stuck with a few issues.
Firstly, I'm not even sure my path is correct; i want to get a drawable file and store it to my database and there must be an easier way than doing a path straight from the C drive right?
Secondly, I don't know much about this but i need to convert my file to a bitmap so that it can be converted to a byte array? And i'm not sure how to do this exactly.
I've tried several things, wrote this code out about 10 times already in different ways and not getting anywhere. Thanks all for help in advance.
public void insertAvatar(String Email, byte[] head) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
String sql = "INSERT INTO Avatar VALUES (?, ?)";
File head = new File("C:\\Users\\PC\\Desktop\\capaa\\src\\main\\res\\drawable\\head2.png");
Bitmap imageToStoreBitmap = head; // doesn't work as my file isnt a bitmap yet
objectByteArrayOutputStream = new ByteArrayOutputStream();
imageToStoreBitmap.compress(Bitmap.CompressFormat.JPEG, 100, objectByteArrayOutputStream);
imageInBytes = objectByteArrayOutputStream.toByteArray();
contentValues.put("Email", Email);
contentValues.put("head", imageInBytes);
long checkIfQueryRuns = db.insert("Avatar", null, contentValues );
}
You need to use Blob to store images in your SQLite database.
Create a table to store the images
CREATE TABLE " + DB_TABLE_NAME + "("+
KEY_NAME + " TEXT," +
KEY_IMAGE + " BLOB);";
To store an image in the table
public void addImage( String name, byte[] image) throws SQLiteException{
ContentValues cv = new ContentValues();
cv.put(KEY_NAME, name);
cv.put(KEY_IMAGE, image);
database.insert( DB_TABLE_NAME, null, cv );
}
As you can see before inserting the image to the table, you need to convert the bitmap to a byte array.
// To convert from bitmap to byte array
public static byte[] getImageBytes(Bitmap bitmap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 0, stream);
return stream.toByteArray();
}
To retrieve image from database
//search your image using the key and get the cursor
............
byte[] image = cursor.getBlob(1);
............
As you can see the image returned is in a byte array. Now you can convert this byte array to bitmap to use in your app.
//To convert from byte array to bitmap
public static Bitmap getImage(byte[] image) {
return BitmapFactory.decodeByteArray(image, 0, image.length);
}
Having written the answer, I myself is not a big fan of storing images in the database. I am not sure what is your need for storing the images but you can check the following libraries to handle images in your app.
https://github.com/bumptech/glide/
https://square.github.io/picasso/
https://github.com/nostra13/Android-Universal-Image-Loader

App crash when selecting more than 10 images

I am coding an android app using android studio - java , I am facing a problem when picking more than 10 images from gallery and inserting them into Sqlitedatabase table , when inserting the 10th image it takes much longer time than the last 9 images , and when selecting all the images (10) from the table the app crashes with errors
E/SQLiteCursor: onMove() return false. RequiredPos: 9 WindowStartPos: 9 WindowRowCount: 0(original count of query: 10)
Caused by: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 10
Code
Picking and inserting images code
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode== 1 && resultCode == RESULT_OK) {
try {
ClipData clipData = data.getClipData();
if (clipData == null)
return;
for (int i = 0; i < clipData.getItemCount(); i++) {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), clipData.getItemAt(i).getUri());
byte[] myImg = getBitmapAsByteArray(bitmap);
DatabaseHelper databaseHelper = new DatabaseHelper(this);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("myImage",myImg);
db.insert("myTable",null,contentValues);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
getBitmapAsByteArray()
private static byte[] getBitmapAsByteArray(Bitmap bitmap) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
return outputStream.toByteArray();
}
Selecting images from database
public void showImages(View view) {
DatabaseHelper databaseHelper = new DatabaseHelper(this);
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from myTable",null);
cursor.moveToFirst();
Toast.makeText(this, ""+cursor.getCount(), Toast.LENGTH_SHORT).show();
ArrayList<Bitmap> bitmaps = new ArrayList<>();
if (cursor.getCount()>0)
while (!cursor.isAfterLast())
{
bitmaps.add(BitmapFactory.decodeByteArray(cursor.getBlob(0), 0, cursor.getBlob(0).length));
cursor.moveToNext();
}
ViewPagerAdapter adapter = new ViewPagerAdapter(this,bitmaps);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(0);
cursor.close();
}
any suggestions ?
Not sure about the code, but i would not store image directly in Database. Image can be really huge, it might take some time to write and read multiple images.
Instead, like what backend developers do, i would store image as File on Filesystem (app private directory). They will be deleted when app is deleted. Save the images as Files and store their path or name into DB. So whenever you need image, just do:
List<String> getImageNamesFromDb()
List<File> getImageFiles(List<String>)
processYourImageFiles(List<File>)
It is an index out of bound exception.
Your loop is going in negative , you are accessing a row at index -1 which does not exist.
Try errors in your loop.
Please give your code of java file.
It might help more for finding more errors.

Image uploading from android to PHP server using retrofit

I am uploading image to server using retrofit. I am encoding image to bitmap and then convert bitmap to string and passing string to PHP. On PHP side I decode image again and then save to server folder.
It works perfectly if I compress image quality to 30 but app crashes and shows null pointer if I set image quality to 100.
Here is my Code:
ResultActivity:
if (requestCode == 1 && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
// photo = BitmapFactory.decodeFile(picturePath);
profile_photo =
ImageUtils.getInstant().getCompressedBitmap(picturePath);
Uri tempUri = getImageUri(this, profile_photo);
cursor.close();
profile_image.setImageResource(android.R.color.transparent);
Picasso.get()
.load(tempUri)
.resize(150, 150)
.into(profile_image);
profile_image.setScaleType(ImageView.ScaleType.FIT_XY);
profile_image.setPadding(5, 5, 5, 5);
//Bitmap profile_photo = ((BitmapDrawable)
profile_image.getDrawable()).getBitmap();
upload_profileimage();
b.dismiss();
}
Bitmap to string:
public String BitmapTOString(Bitmap bitmap) {
Bitmap bm = bitmap;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteFormat = stream.toByteArray();
String imgString = Base64.encodeToString(byteFormat, Base64.DEFAULT);
return imgString;
}
Retrofit API call:
call = user_profileimge_interface.profileImage_uplaod(BitmapTOString(profile_photo), user_id);
PHP Code:
$data = $baseurl.'user_profile_pictures/'.$user_id.".JPEG";
file_put_contents($data, base64_decode($profile_picture));
echo json_encode(Array('message' => "image inserted"));
API interface:
#POST("update_profilepic.php")
Call<Profile_Image_updateJson> profileImage_uplaod(#Query("profile_picture") String profileImage,
#Query("user_id") String user_id);
I'd suggest sending bitmap as binary data rather than converting to/from string. For example:
#POST
Call<Profile_Image_updateJson> profileImage_uplaod(#Query("user_id") String user_id, #Body RequestBody body);
and then something like:
requestBody = RequestBody.create(MediaType.parse("image/jpeg"), imageBytes)
call = user_profileimge_interface.profileImage_uplaod(user_id, requestBody);
Try to Perform BitmaptoString() operation in a separate thread, away from the Main UI Thread.
As processing bitmap is too costly if you perform it in the Main UI Thread, the App might crash. Also, you can use Asynctask or Any Background Process to perform costly functions and avoid any costly operations in Main Thread.

how to check image size less then 100kb android

I am trying to get image from gallery and setting up it on ImageView , Hear is okay well i get and set image on ImageView, but now i want to check image size of selected image in kb so i set the validaion for image uploading.
Please anyone can suggest me how to check selected image size less then 100kb or not?,
Hear is my code for image selecting and setting it.
Choosing Image useing Intent
Intent iv = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(iv, RESULT_LOAD_IMAGE);
and get Image Result code ..
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
picturePath = cursor.getString(columnIndex);
cursor.close();
Bitmap bmp=BitmapFactory.decodeFile(picturePath);
ivLogo.setImageBitmap(bmp);
uploadNewPic();
}
}
to know the size is less then 100kb. you should know the image size to compare. there is some method to know the size of bitmap
method 1
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
Bitmap bitmap = bitmapOrg;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] imageInByte = stream.toByteArray();
long lengthbmp = imageInByte.length;
method 2
File file = new File("/sdcard/Your_file");
long length = file.length() / 1024; // Size in KB
For more Study
go for http://developer.android.com/reference/android/graphics/Bitmap.html#getByteCount%28%29
Get file size as
File img = new File(picturePath);
int length = img.length();
it will return size in bytes. you can convert byte into kb
ArrayList<String> filePaths = new ArrayList<>();
ArrayList<String> newFilePath = new ArrayList<>(); //for storing file path which size is less than 100 KB
if (imagePaths != null) {
filePaths.addAll(imagePaths);
for (int i = 0; i < filePaths.size(); i++) {
File file = new File(filePaths.get(i));
int file_size = Integer.parseInt(String.valueOf(file.length() / 1024)); //calculate size of image in KB
if (file_size < 100)
newFilePath.add(filePaths.get(i)); //if file size less than 100 KB then add to newFilePath ArrayList
}
}
Here imagePaths stores path of all images that we have selected. Then if imagePaths is not null then add all images path in filePaths. You can use this code for document type of file also.
Just input URI from the intent and get the size of any file
uri = data.getData();
Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
Log.e("TAG", "Name:" + returnCursor.getString(nameIndex));
Log.e("TAG","Size: "+Long.toString(returnCursor.getLong(sizeIndex)));
It will give size in bytes, So 100kb will be 100000bytes. I think this will help you.

Convert Byte to Bitmap

I have to convert a byte to Bitmap and i set it to imageview
i do have methode that convert Bitmap in ImageView into Byte and i insert it later,
public static byte[] ConvertDrawableToByteArray(Drawable drawableResource) {
Bitmap imageBitmap = ((BitmapDrawable) drawableResource).getBitmap();
ByteArrayOutputStream imageByteStream = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageByteStream);
byte[] imageByteData = imageByteStream.toByteArray();
return imageByteData;
}
and when i want to retrieve image from database and show it in an ImageView i do
//--
byte[] image_b = c.getBlob(4);
Bitmap b = BitmapFactory.decodeByteArray(image_b, 0, image_b.length);
img.setImageBitmap(b);
but it return nothing
what is wrong, please help
Thanks a lot
I guess it's because the cursor didn't return anything. Maybe the Blob is too big to be queried in one time because android cursor limit is 1 mb per query please check again your stacktrace/logcat and if the blob size is the problem you can try to split the byte to smaller size and store it in your database
You can play around with the following and let us know if it works:
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = resolver.query(imageUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
return BitmapFactory.decodeFile(picturePath);
I dont understand where is the problèlme i do likee this :String t = image_b.toString(); System.out.println("what it give me" +t); and i find in the logcat this line : 03-19 17:01:28.167: I/System.out(1019): what it give me[B#41f1f5c0
is that mean it return something but dont showin or is the byte [B#41f1f5c0 is not correct

Categories