Zákazník webovej aplikácie napísanej v Go mal niekoľko stoviek wordowských dokumentov, v ktorých mal data, ktoré chcel naimportovať do aplikácie. Táto úloha sa dá vyriešiť priamo v Go.
Dokument vo Worde (.docx) je v podstate zip súboro, ktorý obsahuje niekoľko xml dokumentov. Samotné dáta dokumentu sú uložené v document.xml. Pretože sa jedná o upload súboru, tak je vhodné riešenie to urobiť celé v pamäti a neukladať súbor zbytočne do temporary súboru.
Riešenie je v princípe jednoduché - tu je zjednodušený kód:
// c: controller z frameworku BeeGo má funkciu GetFile
// ktorá vracia https://golang.org/pkg/mime/multipart/#File
docx, fileHeader, err := c.GetFile("file")
if !strings.HasSuffix(fileHeader.Filename, "docx") {
//ak nie je docx súbor tak sa ďalej nepokračuje
}
//rozzipovanie súboru
buf := new(bytes.Buffer)
fileSize, err := io.Copy(buf, docx)
if err != nil {
return
}
r, err := zip.NewReader(bytes.NewReader(buf.Bytes()), fileSize)
if err != nil {
return
}
//prehľadávanie rozzipovaného súboru a hľadanie document.xml
for _, f := range r.File {
if f.Name == "word/document.xml" {
xmlFile, err := f.Open()
if err != nil {
log.Fatal(err)
}
parser := xml.NewDecoder(xmlFile) //parsovanie xml súboru
.
.
.
Pôvodne som si myslel, že to v Go nezvládnem a budem musieť použiť nejakú knižnicu v inom jazyku. Ukázalo sa, že je to nielen možné, ale aj jednoduché. Navyše je to aj rýchle riešenie. Neskôr sme spracovávali všetky súbory naraz a 400 Word dokumentov bolo spracovaných a uložených do databázy za 20 sekúnd.
Globálne packages
Na rozdiel napríklad od Javy, je v Golangu názov package naozaj cestou kde ho nájdete na internete. Napríklad package github.com/olekukonko/tablewriter zapíšte ako
import "github.com/olekukonko/tablewriter"
V Jave sa používa podobný princíp pri definovaní názvov, ale jedná sa len o konvenciu. V Go ma hlbší význam. Takto definovaný package si nainštalujte jednoduchým príkazom
go get github.com/olekukonko/tablewriter
Alebo môžete dať iba go get a nainštalujete si všetky knižnice použité v projekte. Takto môžete organizovať aj zdrojové kódy v rámci organizácie.
Celé toto riešenie do veľkej miery nahradí potrebu Mavenu v Go.
Duplicitný názov package
Autori Go odporúčajú aby názov package bol čo najvhodnejší. A to aj v situácii, že by ste mali v projekte rovnaké názvy. Aj priamo v systémových knižniciach sú napríklad dva packages s názvami rand. Go má jednoduchý spôsob ako to používať.
package main
import (
r "crypto/rand"
"fmt"
"math/big"
"math/rand"
)
func main() {
fmt.Println(rand.Intn(1000))
max := *big.NewInt(1000)
n, _ := r.Int(r.Reader, &max)
fmt.Println(n)
}
Zacyklený import
Jazyk Go núti programátorov programovať čisto. Jedná z vecí je, že ak v package A importujete package B, v package B importujete package C, v package C importujete package D tak v package D sa vám nepodarí naimportovať package A.
To je vec, kvôli ktorej musíte v Jave použiť Maven a aj tak to riešenie nie je tak dôsledné ako v Go.
Toto je podľa mňa tá najúžasnejšia vlastnosť na celom Go pri práci na väčších projektoch. Musíte si celú aplikáciu poctivo rozdeliť na nezávislé moduly a ste vlastne donútení pracovať tak ako sa má.
Zbytočný import
Tento program je v poriadku:
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("Náhodné číslo")
fmt.Println(rand.Intn(1000))
}
A tento ani neskompilujete:
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("Náhodné číslo")
//fmt.Println(rand.Intn(1000))
}
Chyba: - imported and not used: "math/rand"
Go má na posielanie mailov package net/smtp. Posielanie cez tento package je jednoduché (a niektoré utilitky robia postup ešte jednoduchší) - tu je príklad z dokumentácie:
package main
import (
"log"
"net/smtp"
)
func main() {
// Set up authentication information.
auth := smtp.PlainAuth("", "user@example.com", "password", "mail.example.com")
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
to := []string{"recipient@example.net"}
msg := []byte("To: recipient@example.net\r\n" +
"Subject: discount Gophers!\r\n" +
"\r\n" +
"This is the email body.\r\n")
err := smtp.SendMail("mail.example.com:25", auth, "sender@example.org", to, msg)
if err != nil {
log.Fatal(err)
}
}
To ale funguje iba pri štandardne nainštalovaných serveroch. Firemné servery v lokálnych sieťach majú často vypnutú authentifikáciu, takže sa neposiela meno a heslo. To urobíte takto:
err := smtp.SendMail("mail.example.com:25", nil, "sender@example.org", to, msg)
Bežne ale aj narazíte na situáciu, keď sa používa TLS protokol a adresa servera je iná ako jeho certifikát alebo je tam iný problém s certifikátom. Vtedy sa musí vypnúť TLS kontrola. V ďalšom príklade je použitá knižnica go-gomail a vypnutie TLS kontroly:
package utils
import (
"crypto/tls"
"gopkg.in/gomail.v2"
"testing"
"time"
)
func TestSendMail2(t *testing.T) {
m := gomail.NewMessage()
m.SetHeader("From", "rioFrom@example.com")
m.SetHeader("To", "rioTo@example.com")
m.SetHeader("Subject", "Hello! "+time.Now().Format(time.Stamp))
m.SetBody("text/html", "Ahoj <b>Jano</b> a <i>Fero</i>!")
d := gomail.Dialer{Host: "smtp.example.com", Port: 25, TLSConfig: &tls.Config{InsecureSkipVerify: true}}
t.Log("TLS", d.TLSConfig)
if err := d.DialAndSend(m); err != nil {
panic(err)
}
}
Keď som pred piatimi rokmi prvý krát videl Go, tak ma odradil malý počet open source knižníc. Teraz je knižníc celkom dosť, ale aj tak som mal obavy či dokážem urobiť to čo som potreboval:
- Urobiť funkciu, ktorá vyrobí obrázok QR kódu
- Vyrobiť PDF a nakresliť tam ten obrázok
- Ideálne to urobiť všetko bez potreby ukladania do súboru, pretože to má byť webová služba
Nakoniec to nebol žiadny problém. Tu je výsledný program aj s ukážkou zápisu pdf do súboru.
package main
import (
"bufio"
"bytes"
"code.google.com/p/rsc/qr"
"github.com/jung-kurt/gofpdf"
"io/ioutil"
)
func main() {
b := CreatePdf("http://riom-blog.logdown.com/")
ioutil.WriteFile("testqr.pdf", b, 0777)
}
func CreatePdf(qrc string) []byte {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
code, err := qr.Encode(qrc, qr.H)
if err != nil {
panic(err)
}
pdf.RegisterImageReader("qrimg", "PNG", bytes.NewReader(code.PNG()))
pdf.Image("qrimg", 50, 50, 50, 50, false, "PNG", 0, "")
var b bytes.Buffer
w := bufio.NewWriter(&b)
pdf.Output(w)
pdf.Close()
w.Flush()
return b.Bytes()
}
BTW fotky ľudí, ktorí skenujú QR kód: http://picturesofpeoplescanningqrcodes.tumblr.com/
Za jazykom Go stoja fascinujúci ľudia:
Ken Thompson - jeho najznámejší projekt je Unix ale urobil napríklad aj databázu šachových koncoviek.
Rob Pike - pred mnohými rokmi urobil práve s Kenom Thompsonom špecifikáciu UTF8.
Pre mňa osobne, je zaujímavé, že budúci mesiac vyjde kniha od Briana Kernighana. Na obrázku je kniha, z ktorej som sa učil ešte za socializmu, ktorú pôvodne napísal spolu s Denisom Ritchie v roku 1978 a tu je odkaz na jeho novú knihu o Go.
Kniha je už teraz na Amazone bestseller číslo 1 v kategórii Computer Programming Language & Tool