Search code examples
pythonweb-applicationsbinarybackendqr-code

Generate QR code in Django backend and send it to ReactJS frontend without saving QR code on server


I am working on a project where I have a web application frontend = React JS and backend = Django (python). Now I have the task of generating QR codes that will contain JSON with information about the product.

I used the "qrcode" library in python, modified the data appropriately, and saved it using the library's functions in the QR code. When I tried to save the QR to the server and load it, everything works as it should.

Now I need to transfer the QR to the frontend. I would like to try this without saving the QR image on the server, just transferring the data which is then rendered on the frontend. here is the code I tried BACKEND :

class generateQR(APIView):
    permission_classes = (IsAuthenticated, )
    
    def get(self, request, software_id):
        user = request.user
        try:
            software = Software.objects.get(id=software_id)
            
            location= software.location.all()
            namelocation=[l.namefor l in location] # get location.nameatribut from object location

            qr_data={
                "kcm":software.kcm,
                "name":software.name,
                "admin":software.admin.name,
                "location":name,
            }
            #print("QR data:",qr_data)
            
            qr=qrcode.QRCode(
                version=1,
                error_correction=qrcode.constants.ERROR_CORRECT_L,
                box_size=10,
                border=4,
            )
            qr.add_data(qr_data)
            qr.make(fit=True)
            img=qr.make_image(fill_color="black", back_color="white")
            img_data=BytesIO()
            img_data.seek(0)

            #img.save("testQR.png") # save localy to server, I want to avoid it

            img.save(img_data) # save to "img_data" the created "img" if I tried "img.save(img_data, format="PNG")" - it failed here  
            img_data.seek(0) # set BytesIO pointer to the begining
            img_binary=img_data.getvalue()
       
            print("img binray: ",str(img_binary))
            return Response({"qr": str(img_binary) }, status=status.HTTP_200_OK)
        except : 
             ..........code continues ............

and I have this part on the frontend:

{/* .... some code above ....*/}

const generateQRCode=async()=>{
      //Call backend endpoint for generate QR code
      console.log("sending with id:",id);
      try{
        const response = await axios.get(API_SOFTWARE+id+"/genQR"); //,{ responseType: 'arraybuffer' }
        console.log("resp: ",response.data);
        //const dataconverted = new Uint8Array(response.data);
        //console.log("converted data: ", dataconverted);
        const blob = new Blob([response.data.qr]); //transfer binary to blob , { type: 'image/png' }
        console.log("blob: ",blob);
        const url = URL.createObjectURL(blob); //create url for show QR
        console.log("url: ",url);
        setQRCode(url);

      }catch (err){
        console.log("Error in generating QR code", err);
      }
    };

{/* ..... code continues ....*/}

{/* Show QR code if it exists */}
{/* ..... in return some code above ....*/}
          {QRCode && <img src={QRCode} alt="QR" />}
          <Button variant="secondary" onClick={generateQRCode}>
            Generate QR
          </Button>
{/* ..... code continues ....*/}

When I tried to debug where the problem is I found that I get the same data as generated from the backend in the response.

These data are:

b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\xea\x00\x00\x01\xea\x01\x00\x00\x00\x00\xe3\x160\x97\x00\x00\x03\x18IDATx\x9c\xed\xdaIn\xc4 \x10@Q$\x1f\xc0G\xea\xab\xfbH>\x80\xa5JCMx\x88\x94\x86^d\xf1Y$\x1exxS\x02\xaa\xe8"3m+p8\x1c\x0e\x87\xc3\xe1\xf0\xff\xc4\x8b\xb5E\xf2V\xb6\xf5x\xbf\xdd\xebU\xbd\x7f\xd5\xabz\xab\xed\x05\x87O\xf2v#\xfb"\xfe\xb8\xa1z\xbb\xdb@\xef\xabv\xeb_\x80\xc3\xa7\xf9^\xbc\x83\xbc\xbb\x96\xb5F\xee\xda\xdeE\xe4j\x0c/\xd2\x82\x1b\x0e\xff\x1e\x7f\xf7j\x8f-@}\xfa<b\xaa\x84\xc3\xbf\xce\xdf\xa1z\xc4@\x87\xce\x97\xddh/8\xfc{\xbc\xfdK.\xf6\xc7\x17\xedz%>n\n8|\x82G2\xd2\xc5\xe6\xf3\x9f\xc7d\x04\x0e\xff\x9cg\xb3\xf0\xd5\xbd\xa1m\x10=\xf1]\xe4\xda\xe0\xf0q\xde\xbaj\xce\x11]-,3-\x89\xa0\xd5\r"\x1c>\xc7m\xe3\xd7.\x1b_Ero\x98\xa3i\xe4n\xb7\xa9\x12\x0e\xff\x9ck\x87\x12\x89\x87\xf5?\xb4\x97\xff9J\xff\x198|\x8e\xb7yPk.\x12\xa1j\x19\xb1\xf8m|\xe6>U\xc2\xe1\xa3\\\xbc\x7f\xae\xc3\xfd,\xb9\x06\x97K\n\x0c\x87\x0f\xf0\x8cC\x9d k\x13\xd1\xb9\xd1\xd7\xeb\xb6s\xdc<\x86\xf5\xa3p\xf8\x04/YP^s\x8c\xa5C\xfd\xa6\xb1\xb6\x17\x1c>\xc7\xed\x18\xc3\x831\xa5_I\xa4 G\x047\x1c>\xc5m\xaa\xdc\xb3\xc3\xe5,\xad\xd8mL\x9f\xf6U8|\x98\xdb\xe3K\xe2\x11\xcb\xf2y\xbf\xe8\xe1\x0b\x87\xcf\xf1\xcd\x17h{\xbc&\xcf\x99\xb3\xff\x0c\x1c>\xcb\xef\xe8\xe8\xc6\xb5\x12L\xb1,\xf9)\x97\x81\xc3\x07\xb8\x16^|\n\xcc\xea\x9fN\x8b]}\xaf\x14\xab\xd2\xc0\xe1s<\x9f\xe8\xb60r\x93\x8c\xe6\x13\xbf\xad\xefp\xf8\x00\x97\xb6\x0e\xd7\xb6\xdahY\x8c)VZ\xceqs48|\x94[\xa6\xe1E>[\x82#\xf1\xed\xf6\x8b:s\xe6-\x1c>\xcc\xb5\xdc\x12\x99\xee%\x05\x891\xc4\xaa/\xd7\xa0\x85\xc3?\xe7\xa7J\x9f\xef\x12\xbb\x9asd\xbfY\x07\x84\xc3g\xb9\xae\xb9\xf1\x93\x81\x97\xbe\xcbe\xb9\xbd]t\x0c\x9fW\xe1\xf0)n\xc5\xe3\xd5\xc2\xb2\xb5\xb6\x18[\x1e\x9c\xc5\xc0\xbd<\x96\xfb\xe0\xf0\x01.Q}\x119\x97\xf6|\xaal\x19q\\Y\xac\xc3\xe1\xc3\\gI[\x87E\xb3_\x8b\\\x8fW\x1f\xf7)h\xe1\xf0\x01n\xfb@\xef\xaf\xc9\xb0v\x88\xa0\xd5\x05Z\x0f9J\x81\xc3g\xb9\x15Y"hM\x1e\xbaJ\xe7\xf2\xdd\r\t\x87OrKA\xda\xb9\x99\x87\xaf\xe7\xc1mp\xdb\x11Z\xbc^\x8f6\xe0\xf0\x01\x9e\xad\xe81\x86\x97\x91O\x85?\xff\x05U7\x91\xc2\xe1\xc3\xdc\xc3q\xf1<D\xa4\xeb\xdf\xad\xcdy\xaa!p\xf8$\xefz\xf9\x95\xa7\xbb\x0f\x81\\n3-\x1c>\xc0u\x81\x8e\x15Y[\xfex\xa0\xb6U\xfa+8\xfck\xbc\xbd\x96<\xc1\x88\x99\xf3\x94\x9b\xc438\xfc\x0b\\$N\xd0\xf2,C\xba\x17V\x07\xfc-\xe6\xe1\xf0\xbf\xf3\xf6o\xb7\x9c\xc3\x02\xb4\xcbC\xc4\xd3b\xb1b\xe0%h\xe1\xf0\xcf\xb9\xb5S\xba\xb1\xe5\xb3v\x9b\xcb\xf2\xed<\x0e\x0e\xff\x9c\x8f68\x1c\x0e\x87\xc3\xe1p\xf8\x7f\xe1?v\xf5\xeb\xd7Ta4\xa8\x00\x00\x00\x00IEND\xaeB`\x82'

I'm not sure, but this data looks suspicious and I was unable to convert it to an image using online tools.

According to the submitted code above, I think:

I think there is a bug somewhere in storing the data in binary form.

I would be very grateful for any advice or help. Maybe even a different solution than what I have thought of here. Thank you in advance

EDIT:

I tried to reconstruct the binary data into an image:

            image=Image.open(BytesIO(img_binary))

            image.save("reconstructedQR.png","PNG")

And the reconstructed picture is working so the problem is not in data.

Now I will try to findout if the problem is not in frontend in converting binary to picture.

EDIT 2:

I findout little mistake in received data in response: There was on the start b' and on the end ' which are from python binary type. So I deleted it and tried to use these binary daty. I striped it with:

const binaryDataString=response.data.qr.substring(2, response.data.qr.length - 1);

This successfully works but the converted data in blob are corupted. Now I need to findout how to get the image from these data.

EDIT 3:

I chose a different approach for the transfer, namely base64:

img_binary=base64.b64encode(img_data.getvalue()).decode('utf-8')

Now I have to decode it and display on frontend:

console.log("resp: ",response.data.qr);

const binaryDataString=atob(response.data.qr);
const blob = new Blob([binaryDataString]);
const url = URL.createObjectURL(blob);

setQRCode(url);

When I try to decode data in cyberchef which I console loged. The PNG file is good and it contains QR code which I want! But the creation of blob and putting it to url is not working and saying that data are corupted.

EDIT 4:

I found out the problem. There is problem in frontend in converting base64 back to binary data. I saved these converted data and compare it with original send from backend and there are some differences in character for example:

‰PNG --> PNG &IDATxœíÚKr¬0 --> IDATxíMnë8

And more.... So I need to findout how to convert it without these differencies.


Solution

  • SOLUTION:

    I found this Stackoverflow, where is solved creating png from base64 in JavaScript:

    Stackoverflow

    So I transformed my frontend code to :

            const binaryDataString=atob(response.data.qr);
            const byteNumbers = new Array(binaryDataString.length);
    
            for (let i = 0; i < binaryDataString.length; i++) {
              byteNumbers[i] = binaryDataString.charCodeAt(i);
            }
    
            const byteArray = new Uint8Array(byteNumbers);
            const blob = new Blob([byteArray], {type: 'image/png'});
            const url = URL.createObjectURL(blob);
    
            setQRCode(url);
    
    

    Just for having it in one place (answer) there is backend: I prepared my data and saved them with library qrcode to QR code.

    I saved the created QR code (bytes) to variable img_data then before sending them to the frontend I coded them to base64.

                img_data=BytesIO()
                img_data.seek(0)
                img.save(img_data) 
                img_data.seek(0) # set BytesIO pointer to the begining
                img_binary=base64.b64encode(img_data.getvalue()).decode('utf-8')
    
                return Response({"qr": (img_binary) }, status=status.HTTP_200_OK)
    
    

    This is working solution for my project.