Pembukaan: Kenapa Streaming dan Validasi Penting
Ketika aplikasi menerima file Excel dari klien, tantangan utamanya adalah menjaga keamanan, menghemat memori, dan memastikan data terproses dengan benar. Go Fiber v3 menawarkan performa tinggi untuk API, tetapi tanpa strategi streaming dan validasi yang tepat, sebuah unggahan besar bisa menghabiskan RAM, memicu DoS, atau memasukkan data kotor ke sistem.
Artikel ini membahas praktik yang dapat langsung Anda terapkan: upload multipart via API, validasi ukuran dan tipe file sebelum pembacaan, membaca konten dengan streaming melalui Excelize, memetakan kolom ke struct, melakukan validasi baris demi baris, mengelola partial success, serta menambahkan ringkasan hasil import. Kita juga menyorot aspek keamanan seperti batas memori atau pencegahan file berbahaya.
1. Desain Endpoint Upload dan Pertimbangan Sinkron vs Asinkron
Gunakan endpoint yang jelas: POST /api/import/excel untuk sinkron (response langsung) atau POST /api/import/excel-job untuk asinkron (mengembalikan ID pekerjaan). Untuk sinkron, batasi ukuran file di middleware Fiber, dan block request jika melebihi batas mantap (misal 10MB). Untuk asinkron, terima file, simpan ke storage sementara (disk, S3), lalu buat job queue yang membaca file dengan worker. Begitu job selesai, kirim notifikasi atau klien dapat memanggil endpoint status.
Contoh desain:
- Sinkron: Ideal untuk file kecil (< 5MB) atau validasi cepat.
- Asinkron: Cocok untuk file besar (> 10K baris) agar tidak memblokir worker Fiber.
2. Multipart Upload dan Validasi Awal
Validasi middleware Fiber
Gunakan middleware Fiber untuk membatasi ukuran request body. Dalam app.Use(), panggil func(c *fiber.Ctx) error yang memeriksa header Content-Type dan Content-Length.
app.Use(func(c *fiber.Ctx) error {
max := int64(10 << 20) // 10MB
if c.Request().Header.ContentLength() > max {
return fiber.ErrRequestEntityTooLarge
}
if !strings.HasPrefix(string(c.Request().Header.ContentType()), "multipart/form-data") {
return fiber.ErrBadRequest
}
return c.Next()
})Validasi tipe file
Pada handler, pemeriksaan tambahan dapat dilakukan dengan mengecek ekstensi dari file.Filename dan mime.TypeByExtension. Namun, jangan hanya mengandalkan nama karena bisa dipalsukan. Selalu baca beberapa byte awal (magic number) sebelum memproses lebih lanjut.
Contoh memverifikasi header:
func isExcel(content []byte) bool {
return bytes.HasPrefix(content, []byte("PK")) // Excel xlsx memakai zip
}
// Setelah mengambil file multipart:
buffer := make([]byte, 512)
file.Read(buffer)
if !isExcel(buffer) {
return fiber.NewError(fiber.StatusBadRequest, "Tipe file tidak didukung")
}3. Membaca Excel dengan Streaming Efisien
Pustaka github.com/xuri/excelize/v2 menyediakan API streaming untuk membaca file besar tanpa mengonsumsi memori besar. Gunakan file.NewStreamReader(sheetName) agar baris dibaca satu per satu.
f, err := excelize.OpenReader(file)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Gagal membuka file Excel")
}
streamReader, err := f.NewStreamReader("Sheet1")
if err != nil {
return err
}
for {
row, err := streamReader.Read()
if err == io.EOF {
break
}
if err != nil {
return err
}
processRow(row)
}
Dengan streaming, hanya baris yang sedang diproses berada di memori. Pastikan Anda menutup file dan reader setelah selesai untuk menghindari kebocoran descriptor.
4. Mapping Kolom ke Struct dan Validasi Per Baris
Struktur data target
Definisikan struct yang mewakili data yang diinginkan. Tambahkan tag validation jika perlu.
type ImportCustomer struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
Phone string `validate:"omitempty,e164"`
Balance int64 `validate:"min=0"`
}
Mapping otomatis
Jika kolom Excel konsisten (misal A=Name, B=Email), Anda bisa buat helper untuk mengisi struct dari slice:
func mapRowToStruct(row []string) ImportCustomer {
return ImportCustomer{
Name: strings.TrimSpace(row[0]),
Email: strings.TrimSpace(row[1]),
Phone: strings.TrimSpace(row[2]),
Balance: parseInt(row[3]),
}
}
Gunakan package github.com/go-playground/validator/v10 untuk validasi field.
Validasi per baris
Untuk setiap baris, lakukan validasi dan catat statusnya. Dengan pendekatan ini Anda dapat mengizinkan partial success—beberapa baris valid diproses sementara baris lainnya dilaporkan sebagai error.
validate := validator.New()
for idx, row := range rows {
data := mapRowToStruct(row)
if err := validate.Struct(data); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("Baris %d: %v", idx+1, err))
continue
}
// simpan ke database
if err := repo.CreateCustomer(data); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("Baris %d: gagal simpan %v", idx+1, err))
continue
}
result.Success++
}
5. Partial Success dan Ringkasan Hasil
Selain menghentikan import jika ada error kritis, ciptakan response yang mengomunikasikan hasil secara transparan:
- Total baris: berapa baris diproses.
- Berhasil: jumlah sukses.
- Error: array error per baris.
- Waktu proses: durasi agar klien tahu berapa lama.
Contoh response sinkron:
{
"total": 120,
"success": 115,
"errors": [
"Baris 13: email tidak valid",
"Baris 45: gagal simpan duplicate key"
],
"duration" : "2.3s"
}
Untuk mode asinkron, simpan ringkasan ini di database atau cache dan sediakan endpoint status berdasarkan job ID.
6. Keamanan File Upload dan Batas Memori
Perlindungan terhadap file berbahaya
Jangan langsung menyimpan file ke path yang bisa dieksekusi. Simpan di folder khusus dengan permission terbatas, lalu hapus setelah selesai. Validasi file dengan content sniffing dan jangan jalankan file tersebut.
Batas memori
Fiber menggunakan goroutine per request. Batasi goroutine terbuka dan gunakan streaming agar tidak memori intensive. Jika menangani file besar, simpan sementara ke disk kemudian proses dengan worker, atau gunakan io.LimitReader untuk menghindari pembacaan tanpa batas.
Debugging
Tambahkan logging contextual: nama file, ukuran, user ID. Jika error muncul saat parsing Excel, bantu user dengan memberikan baris dan kolom yang bermasalah. Gunakan context timeout untuk menghindari proses import menggantung.
Kesimpulan
Membangun API import Excel yang aman di Go Fiber v3 membutuhkan perpaduan validasi awal, pembacaan streaming, pemetaan dan validasi per baris, serta dukungan partial success agar pengguna dapat memperbaiki data yang bermasalah tanpa kehilangan hasil lain.
Dengan desain endpoint yang memisahkan mode sinkron dan asinkron, serta mencatat ringkasan hasil import, tim Anda dapat mengelola data batch secara andal. Tetap utamakan keamanan file upload, batasi memori, dan buat respons yang membantu klien menghadapi kegagalan parsial.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!