[Golang] Save File in QR Code


Recently I got an idea to embed file in the QR code, so I tried to find Go QR code encoder/decoder and started to do experiment. For encoder, I found github.com/skip2/go-qrcode. For decoder, I found github.com/tuotoo/qrcode.

My idea is simple, first convert the file content/name into base64 encoding, and then save the content in the QR code. Because the limitation of content size in single QR code image, you can only save small files. For large files, we need to save the content in several QR code images. Since this is only experiment, I use only small files and do not make any segmentation.

My code is as follows:

fileqrcode.go | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package fileqrcode

import (
	"encoding/base64"
	qrencode "github.com/skip2/go-qrcode"
	"github.com/tuotoo/qrcode"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

// ======================
// Save file into QR code
// ======================

func filenameToBase64(filepath string) string {
	filename := path.Base(filepath)
	return base64.StdEncoding.EncodeToString([]byte(filename))
}

func FileToQrcode(filepath string) (err error) {
	b, err := ioutil.ReadFile(filepath)
	if err != nil {
		return
	}

	outputFilename := filenameToBase64(filepath) + ".png"
	f64 := base64.StdEncoding.EncodeToString(b)
	err = qrencode.WriteFile(f64, qrencode.Medium, 256, outputFilename)
	return
}

// ==========================
// Retrieve file from QR code
// ==========================

func getFilenameFromBase64(encoded string) (filename string, err error) {
	b, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return
	}
	filename = string(b)
	return
}

func getFileContentFromBase64(encoded string) ([]byte, error) {
	return base64.StdEncoding.DecodeString(encoded)
}

func QrcodeToFile(filepath string) (err error) {
	filename := path.Base(filepath)
	ext := path.Ext(filename)
	oriFilename, err := getFilenameFromBase64(strings.TrimSuffix(filename, ext))
	if err != nil {
		return
	}

	f, err := os.Open(filepath)
	if err != nil {
		return
	}
	defer f.Close()

	qrmatrix, err := qrcode.Decode(f)
	if err != nil {
		return
	}

	content, err := getFileContentFromBase64(qrmatrix.Content)
	if err != nil {
		return
	}

	return ioutil.WriteFile(oriFilename, content, 0644)
}

It looks fine, but when I run the following test:

Example:

fileqrcode_test.go | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package fileqrcode

import (
	"testing"
)

func TestFileToQrcode(t *testing.T) {
	err := FileToQrcode("path_to_my/robots.txt")
	if err != nil {
		t.Error(err)
	}
}

func TestQrcodeToFile(t *testing.T) {
	err := QrcodeToFile("cm9ib3RzLnR4dA==.png")
	if err != nil {
		t.Error(err)
	}
}

The decoded string from the QR code image is not legal string. I tried to decode the QR code image from online QR code decoder, the decoded string is correct. So the implementation of github.com/tuotoo/qrcode is buggy. Anyway, this is an interesting experiment to embed files in the QR code.


Tested on: Ubuntu Linux 17.10, Go 1.10.

References:

[1]
[2]Animated QR data transfer with Gomobile and Gopherjs : golang