Error saving JPEG image in database as string (Base64)

Asked

Viewed 499 times

1

I’m developing an app that has a form where the user can attach a photo to the form. However I am having difficulty implementing this. In my APK I already capture the image, convert to base 64 and send via Rest to my service.

public void tirarFoto(View view){
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        Bundle bundle = data.getExtras();
        if(bundle != null){
            Bitmap img = (Bitmap) bundle.get("data");
            Toast toast = Toast.makeText(getApplicationContext(), "Foto anexada", Toast.LENGTH_LONG);
            toast.show();
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            img.compress(Bitmap.CompressFormat.JPEG, 100, stream);
            foto = Base64.encodeToString(stream.toByteArray(), Base64.DEFAULT);

        }
    }
}
public void registerForms() {

    final String address = frua.getText().toString().trim();
    final String district = fbairro.getText().toString().trim();
    final String city = fcidade.getText().toString().trim();
    final String email = femail.getText().toString().trim();
    final String complement = fcompl.getText().toString().trim();
    final String state = festado.getText().toString().trim();
    final String note = fobs.getText().toString().trim();
    final String countries = fpais.getText().toString().trim();
    progressDialog = ProgressDialog.show(DenunciaActivity.this, "Aguarde um momento", "Enviando...", true, false);

    StringRequest stringRequest = new StringRequest( Request.Method.POST, REGISTER_URL, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {

            if (response.contains("Erro")) {
                progressDialog.dismiss();
                Toast.makeText( DenunciaActivity.this, "Erro ao enviar", Toast.LENGTH_LONG ).show();

            } else {
                progressDialog.dismiss();
                Intent intent = new Intent(DenunciaActivity.this, MainActivity.class);
                Toast.makeText( DenunciaActivity.this, "Enviado com sucesso!", Toast.LENGTH_LONG ).show();
                startActivity(intent);
            }

        }
    },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    progressDialog.dismiss();
                    Toast.makeText( DenunciaActivity.this, error.toString(), Toast.LENGTH_LONG ).show();
                    Log.i( TAG, "Lat: " + error );
                }
            } ) {
        @Override
        protected Map<String, String> getParams() {
            Map<String, String> map = new HashMap<String, String>();
            map.put( KEY_USERNAME, name );
            map.put( KEY_DATE, dataFormatada );
            map.put( KEY_STATE, state );
            map.put( KEY_CITY, city );
            map.put( KEY_DISTRICT, district );
            map.put( KEY_ADDRESS, address );
            map.put( KEY_EMAIL, email );
            map.put( KEY_COMPLEMENT, complement );
            map.put( KEY_COUNTRIE, countries );
            map.put( KEY_LAT, String.valueOf( latitude ) );
            map.put( KEY_LONG, String.valueOf( longitude ) );
            map.put( KEY_NOTE, note );
            map.put( KEY_STATUS, "ATIVO" );
            map.put( KEY_IMAGE, foto );
            Log.i( TAG, "Lat: " + longitude +" "+latitude);
            return map;
        }

    };

    RequestQueue requestQueue = Volley.newRequestQueue( this );
    requestQueue.add( stringRequest );
}

In my service it’s like this:

public class Services extends Controller {
public static void denuncia(@Valid String nome, String data, String rua, String bairro, String complemento, String cidade, String estado, String pais, String observacao, String email, String latitude, String longitude, Status status, String foto) {
    if (validation.hasErrors()) {
        String mensagem = "Erro ao cadastrar";
        JsonObject j = new JsonObject();
        j.addProperty("Erro", 404);
        j.addProperty("msg", mensagem);
        renderJSON(j);
    } else {
        String msgsucess = "Cadastrado com sucesso!";
        Denuncia denuncia = new Denuncia();
        denuncia.nome = nome;
        denuncia.data = data;
        denuncia.rua = rua;
        denuncia.bairro = bairro;
        denuncia.complemento = complemento;
        denuncia.cidade = cidade;
        denuncia.estado = estado;
        denuncia.pais = pais;
        denuncia.observacao = observacao;
        denuncia.email = email;
        denuncia.latitude = latitude;
        denuncia.longitude = longitude;
        denuncia.status = status;
        denuncia.foto = foto;
        denuncia.save();
        JsonObject j = new JsonObject();
        j.addProperty("Success", 200);
        j.addProperty("msg", msgsucess);
        renderJSON(j);
    }
}

And my model this:

@Entity
public class Denuncia extends Model{

@Required
public String nome;
@Required
public String rua;
@Required
public String bairro;
@Required
public String complemento;
@Required
public String data;
@Required
public String cidade;
@Required
public String estado;
@Required
public String pais;
@Required
public String observacao;
@Required
@Email
public String email;
public String foto;
public String latitude; 
public String longitude;

@Enumerated(EnumType.STRING)
public Status status;
@Enumerated(EnumType.STRING)
public Status statusMsg;
public Denuncia() {
    status = Status.ATIVO;
    statusMsg = Status.NAO_LIDA;
}

The error I am going through is related to the size of the string I am receiving in my database, it is bigger than the supported one, as the output:

Caused by: org.h2.jdbc.JdbcSQLException: Value too long for column "FOTO VARCHAR(255)": "STRINGDECODE('/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\nAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB... (62229)"; SQL statement:
insert into Denuncia (id, bairro, cidade, complemento, data, email, estado, foto, latitude, longitude, nome, observacao, pais, rua, status, statusMsg) values (null, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [22001-193]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.table.Column.validateConvertUpdateSequence(Column.java:335)
at org.h2.table.Table.validateConvertUpdateSequence(Table.java:784)
at org.h2.command.dml.Insert.insertRows(Insert.java:151)
at org.h2.command.dml.Insert.update(Insert.java:114)
at org.h2.command.CommandContainer.update(CommandContainer.java:98)
at org.h2.command.Command.executeUpdate(Command.java:258)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:160)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:146)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:186)
... 30 more

Somebody help me find a way out of trouble?

1 answer

1


You set the image column as VARCHAR(255).

In general the type varchar stores one to two bytes per character (assuming UTF-8).

This means that any image you try to save with more than 123 bytes will give this error. Yes, I said bytes.

I saved your avatar photo here on my machine to see the size. It takes 8192 bytes on disk.

Use a larger column size. And if you’re saving files, make it easy for yourself - try a binary format (blob or something like).

  • How could you do that using that blob? pq this table will have several images, but as I have never done it (upload image to database) I do not know how to implement and which is the best way

  • @Carlosdiego sorry, I am a zero left in Java :( do not know the API.

  • The best way is to save the images on disk and write the path in the database. Saving huge files in the database is not recommended.

  • @Valdeirpsr disagrees with all my forces. Binary types were created for this. If you do not store the files in the base, you will run the serious risk of having inconsistencies over time. And your base backups will not include the files, so any restore on another machine will be invalid without another backup more complex and external to the bank.

  • @Renan although the binary types are created for this, the use of memory will be much greater. Since his project should use a lot of searches in DB, I particularly (I am not DBA), I would choose another way. Of course it can use the database as a safer and more reliable way (it is undeniable that this pro of the database), but it will have to choose to use a cache to avoid the unbridled consumption of RAM and it will probably be on the hard drive. And you can’t forget: "With large database, comes big responsibilities."

  • I leave here some links of why I consider a better solution (especially if you are working alone and do not have a great knowledge in DBA). 1. https://habiletechnologies.com/blog/better-saving-files-database-file-system/; 2. https://dev.mysql.com/doc/refman/5.5/en/optimize-blob.html; 3. https://docs.microsoft.com/en-us/sql/relational-databases/blob/filestream-sql-server and 4. https://softwareengineering.stackexchange.com/a/150787

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.