Search code examples
androidpythonsocketstcpbase64

Can't decode string to image using Base64


In a android application I'm building, I convert the jpg image to string using Base64.encodeToString() and sending to a server via TCP socket.

The problem is when I try to decode the string back to the image. I can print the string that I receive, it looks like this at the end of the file(The only part I can copy because the file is to big to print everything on the terminal):

....+77DgjRKHqbxBmYCDOzv9vLzFwff4N146snCWin6ZlzbN++HJOIIPodB/JTOoc1NjczeqoHwOju
iWdI6ePeSO0ADz46vh4LODnM7FCJYhbTX0TizmNatXvxSFoVzLiqfn19iYjvAPD/AQnRoUxtpJij
AAAAAElFTkSuQmCC

But when I try to decode and save into a jpg file again I get the following error:

Traceback (most recent call last):
  File "tcp.py", line 20, in <module>
    file.write(base64.decodestring(msg))
  File "/usr/lib/python2.7/base64.py", line 328, in decodestring
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding

Here is a piece of my Android application code used to encode and send the message:

//Function that encodes the Bitmapfile into string
    public String BitMapToString(Bitmap bitmap){
        ByteArrayOutputStream baos=new  ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG,100, baos);
        byte [] arr=baos.toByteArray();
        String result=Base64.encodeToString(arr, Base64.DEFAULT);
        return result;
    }

    class myTask extends AsyncTask<Void,Void,Void>
    {
        //Faz a conexao entre aparelho e servidor
        @Override
        protected Void doInBackground(Void... params){

            try
            {
                //create socket and buffer to send the string message
                newSocket = new Socket(ipAdress,5000);
                printWriter = new PrintWriter(newSocket.getOutputStream());
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(newSocket.getOutputStream()));

                //Reads the internal storage image and converts into string using Base64
                File file  = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM+"/Reccoon3D/123.jpg");
                Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                message = BitMapToString(bitmap); //encodes the bitmap
                //sends the enconded image
                bufferedWriter.write(message);
                bufferedWriter.flush();
                bufferedWriter.close();
                newSocket.close();
            }catch (Exception e)
            {
                e.printStackTrace();
            }
            return null;
        }
    }

And here is my python code that receives the message and try to decode it to image again:

import socket
import base64 

host = '192.168.1.16'
port = 5000
tcp=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
orig = (host,port)
tcp.bind(orig)
tcp.listen(1)
file=open('file.png','wb')

while True:
    con, client = tcp.accept()
    print('Conected by', client)
    while True:
        msg = con.recv(1024000) #Initially 1024 but changet so the message
                                #would not be sliced into pieces
        if not msg: break
        #print(msg)
        file.write(base64.decodestring(msg))

    print('Finalizado com sucesso')
    con.close

Solution

  •     msg = con.recv(1024000) #Initially 1024 but changet so the message
    

    This will read up to 1024000 bytes from the socket. It might be less than this. And, the size of the chunks read using recv do not need to have any correlation with the size of the chunks written by the peer, apart from that the total size read over all recv matches the total size sent.

        file.write(base64.decodestring(msg))
    

    A base64 string has always a size which is a multiple of 4 bytes. Even if the decoded data are only one byte the encoded string will be 4 bytes: 2 encoding the input and 2 '=' for padding. If the length of the string used with decodestring is not a multiple of 4 it will assume a bad padding, i.e. raise binascii.Error: Incorrect padding.

    Your code blindly assumes that the length of msg will always be a multiple of 4 since you kind of assume that recv will return all data sent. But, this assumption is wrong and if the length of msg is not a multiple of 4 you get the error you see.