How to Build a Go Web Server for Reading Multiple Barcodes with HTML5 Upload
You can build a barcode-reading web server in Go in about 150 lines: the client picks, drags, or pastes an image, uploads it as a base64 string over a JSON POST, and the Go server decodes every barcode with the goBarcodeQrSDK wrapper around the Dynamsoft Capture Vision SDK (Barcode Reader 11.6.10). The result JSON — text, format, and four-point coordinates — is drawn as overlay rectangles on the page.
What you’ll build: an HTML5 web app (example/web) where users upload images and the Go backend returns decoded barcode locations, using net/http for the server and cgo for native barcode decoding.
Key Takeaways
- Go’s
net/http+ native concurrency handles concurrent barcode decode requests without extra framework dependencies. - The client uploads images as base64 strings; the server decodes them with
goBarcodeQrSDK.DecodeStream()and returns a JSON array withText,Format, andX1..Y4coordinates. - One
CCaptureVisionRouterinstance per request keeps the multi-page/PDF handling simple and idiomatic in Go. - The same
uploadHandlerpattern scales to MRZ, document, and other Capture Vision tasks by swapping the preset template.
Common Developer Questions
How do I read a barcode from an uploaded image in Go?
Decode the base64 payload to bytes, pass it to goBarcodeQrSDK.DecodeStream(imgData), and encode the returned []Barcode as JSON. Each Barcode includes Text, Format, PageId, and the four corners (X1,Y1 through X4,Y4) needed to draw an overlay.
Can a Go web server decode several barcodes in one image?
Yes. CaptureMultiPages inside the SDK returns all barcodes found on an image (or PDF), and the wrapper exposes them as a slice. The example iterates the slice and draws each result on an HTML canvas.
What Go libraries do I need for a barcode web server?
Only the standard library (net/http, encoding/json, encoding/base64) plus github.com/yushulx/goBarcodeQrSDK/v2. The barcode decoding runs natively via cgo; no third-party web framework is required.
Which image formats does the upload endpoint accept?
The endpoint accepts any image the Dynamsoft SDK can decode, including PNG, JPEG, BMP, TIFF and PDF. The HTML file input uses accept="image/*", and the server does not re-encode the bytes — it hands the raw data to DecodeStream.
This article is Part 2 in a 3-Part Series.
Prerequisites
- Go
- Get a 30-day free trial license for Dynamsoft Barcode Reader.
- The goBarcodeQrSDK module (includes the prebuilt bridge for Windows, Linux, and macOS).
HTML & JavaScript for Image Picking, Uploading, and Displaying Results
The user interface includes an input element, a button element, an image element, a canvas element, and a textarea element.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Dynamsoft Vision SDKs</title>
</head>
<body>
<h1>1D/2D Barcode Reader</h1>
<div id="loading-indicator" class="loading-indicator">
<div class="spinner"></div>
</div>
<div class="container" id="file_container">
<div>
<input type="file" id="pick_file" accept="image/*" />
<button onclick="detect()">Detect</button>
</div>
<div class="row">
<div class="imageview">
<img id="image_file" src="default.png" />
<canvas id="overlay_canvas" class="overlay"></canvas>
</div>
</div>
<div class="row">
<div>
<textarea id="detection_result"></textarea>
</div>
</div>
</div>
<script src="main.js"></script>
</body>
</html>

- The input element lets users select an image file.
- The image element displays the selected image file.
- The button element initiates the upload of the image to the server for barcode detection.
- The canvas element is used to draw the contours of the detected barcode.
- The textarea element presents the decoded barcode data.
Picking an Image File from the File System and Clipboard
The loadImage2Canvas function loads the selected image into both the <img> and an Image object sized to the canvas:
let imageFile = document.getElementById('image_file');
let overlayCanvas = document.getElementById('overlay_canvas');
let img = new Image();
function loadImage2Canvas(base64Image) {
imageFile.src = base64Image;
img.src = base64Image;
img.onload = function () {
let width = img.width;
let height = img.height;
overlayCanvas.width = width;
overlayCanvas.height = height;
detect();
};
}
There are three methods for users to add an image:
-
Clicking the input element to select an image file.
document.getElementById("pick_file").addEventListener("change", function () { let currentFile = this.files[0]; if (currentFile == null) { return; } var fr = new FileReader(); fr.onload = function () { loadImage2Canvas(fr.result); } fr.readAsDataURL(currentFile); }); -
Listening for the drag-and-drop event to load an image file.
let overlayCanvas = document.getElementById('overlay_canvas'); overlayCanvas.addEventListener('dragover', function (event) { event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; }, false); overlayCanvas.addEventListener('drop', function (event) { event.preventDefault(); if (event.dataTransfer.files.length > 0) { let file = event.dataTransfer.files[0]; if (file.type.match('image.*')) { let reader = new FileReader(); reader.onload = function (e) { loadImage2Canvas(e.target.result); }; reader.readAsDataURL(file); } else { alert("Please drop an image file."); } } }, false); -
Copying and pasting an image from the clipboard.
document.addEventListener('paste', (event) => { const items = (event.clipboardData || event.originalEvent.clipboardData).items; for (index in items) { const item = items[index]; if (item.kind === 'file') { const blob = item.getAsFile(); const reader = new FileReader(); reader.onload = (event) => { loadImage2Canvas(event.target.result); }; reader.readAsDataURL(blob); } } });
Uploading an Image File as a Base64 String
When the user clicks the “Detect” button, the image is uploaded to the server as a base64 data URL using the Fetch API:
const base64Image = img.src;
const requestBody = {
image: base64Image
};
const response = await fetch('/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
Displaying the Decoded Barcode Data
The server returns the decoded barcode data as a JSON object. The client parses the JSON, draws contours of the detected barcodes on the canvas, and displays the data in the textarea:
const barcodes = await response.json();
detection_result.value = `Found ${barcodes.length} barcode(s)\n`;
barcodes.forEach(barcode => {
detection_result.value += `\nText: ${barcode.Text}, Format: ${barcode.Format}`;
detection_result.value += `\nCoordinates: (${barcode.X1}, ${barcode.Y1}), (${barcode.X2}, ${barcode.Y2}), (${barcode.X3}, ${barcode.Y3}), (${barcode.X4}, ${barcode.Y4})`;
detection_result.value += "\n--------------";
// Draw overlay
context.beginPath();
context.strokeStyle = '#ff0000';
context.lineWidth = 2;
context.moveTo(barcode.X1, barcode.Y1);
context.lineTo(barcode.X2, barcode.Y2);
context.lineTo(barcode.X3, barcode.Y3);
context.lineTo(barcode.X4, barcode.Y4);
context.lineTo(barcode.X1, barcode.Y1);
context.stroke();
context.font = '18px Verdana';
context.fillStyle = '#ff0000';
let x = [barcode.X1, barcode.X2, barcode.X3, barcode.X4];
let y = [barcode.Y1, barcode.Y2, barcode.Y3, barcode.Y4];
x.sort(function (a, b) {
return a - b;
});
y.sort(function (a, b) {
return b - a;
});
let left = x[0];
let top = y[0];
context.fillText(barcode.Text, left, top + 50);
});
Building the Web Server in Go
The Go server hosts the static files, manages image uploads, runs barcode detection, and generates JSON responses.
Serving Static Content
Organize the project directory to include index.html, styles.css, and main.js in a static folder:
/project
/static
styles.css
main.js
index.html
Serve all static content with http.FileServer:
func main() {
fs := http.FileServer(http.Dir("./static"))
http.Handle("/", fs)
...
}
Handling Upload Requests for Barcode Detection
Create the /upload endpoint. The server extracts the base64-encoded image string from the request body, decodes it, and uses the Dynamsoft Barcode Reader SDK to detect barcodes:
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"time"
"github.com/yushulx/goBarcodeQrSDK/v2"
)
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
var data struct {
Image string `json:"image"` // Field where the base64 image data will be stored
}
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
http.Error(w, "Error decoding JSON body", http.StatusBadRequest)
return
}
imgData, err := base64.StdEncoding.DecodeString(data.Image[strings.IndexByte(data.Image, ',')+1:])
if err != nil {
http.Error(w, "Error decoding base64 image", http.StatusBadRequest)
return
}
obj := goBarcodeQrSDK.CreateBarcodeReader()
defer goBarcodeQrSDK.DestroyBarcodeReader(obj)
var ret, errMsg = obj.LoadTemplateFile("template.json")
if ret != 0 {
fmt.Println(`LoadTemplateFile(): `, ret)
fmt.Println(errMsg)
}
startTime := time.Now()
barcodes, err := obj.DecodeStream(imgData)
elapsed := time.Since(startTime)
fmt.Println("DecodeStream() time cost: ", elapsed)
if err != nil {
fmt.Printf(`DecodeStream() failed: %v`, err)
http.Error(w, fmt.Sprintf("DecodeStream failed: %v", err), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(barcodes); err != nil {
// Handle error
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
license := "LICENSE-KEY"
ret, errMsg := goBarcodeQrSDK.InitLicense(license)
if ret != 0 {
fmt.Println(`initLicense(): `, ret)
fmt.Println(errMsg)
return
}
// Serve static files from the "static" directory.
fs := http.FileServer(http.Dir("./static"))
http.Handle("/", fs)
// Additional routes such as "/upload" can be defined here.
http.HandleFunc("/upload", uploadHandler)
// Listen on port 2024.
log.Println("Listening on :2024...")
http.ListenAndServe(":2024", nil)
}
Replace LICENSE-KEY with your Dynamsoft Barcode Reader license. Note that DecodeStream handles both plain images and PDF bytes, and the returned JSON includes the PageId for multi-page files.
Common Issues & Edge Cases
Error decoding base64 image— the client must send a data URL (data:image/png;base64,...); the handler strips everything before the first comma.- Empty barcode results — enable barcode formats explicitly in
template.json(e.g.BF_QR_CODE,BF_DATAMATRIX,BF_ALL) so the decoder scans the formats you expect. - DLL/dylib not found on startup — put the SDK directory on
PATH(Windows),LD_LIBRARY_PATH(Linux), or add the@rpath(macOS) as described in the goBarcodeQrSDK README. - Port 2024 busy — change the
http.ListenAndServeport; the client JavaScript does not reference the port directly.
Source Code
Get the complete sample project source code on GitHub
Disclaimer:
The wrappers and sample code on Dynamsoft Codepool are community editions, shared as-is and not fully tested. Dynamsoft is happy to provide technical support for users exploring these solutions but makes no guarantees.