Add CSV file upload and auto-delimiter detection for pilot import
Allows uploading a CSV file directly instead of only pasting text, and auto-detects comma vs semicolon separators to support more locales.
This commit is contained in:
@@ -164,6 +164,18 @@ func handleDeletePilot(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// detectSeparator counts commas vs semicolons in the first line to pick the delimiter.
|
||||
func detectSeparator(data []byte) rune {
|
||||
sample := string(data)
|
||||
if idx := strings.IndexByte(sample, '\n'); idx > 0 {
|
||||
sample = sample[:idx]
|
||||
}
|
||||
if strings.Count(sample, ";") > strings.Count(sample, ",") {
|
||||
return ';'
|
||||
}
|
||||
return ','
|
||||
}
|
||||
|
||||
func handleImportPilots(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
|
||||
if err != nil {
|
||||
@@ -174,13 +186,34 @@ func handleImportPilots(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusForbidden, "forbidden")
|
||||
return
|
||||
}
|
||||
// Cap the upload to keep memory bounded.
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, 2*1024*1024))
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "read_error")
|
||||
return
|
||||
|
||||
var body []byte
|
||||
if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data") {
|
||||
if err := r.ParseMultipartForm(2 * 1024 * 1024); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "read_error")
|
||||
return
|
||||
}
|
||||
f, _, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "missing_file")
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
body, err = io.ReadAll(io.LimitReader(f, 2*1024*1024))
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "read_error")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
body, err = io.ReadAll(io.LimitReader(r.Body, 2*1024*1024))
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "read_error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
reader := csv.NewReader(strings.NewReader(string(body)))
|
||||
reader.Comma = detectSeparator(body)
|
||||
reader.FieldsPerRecord = -1
|
||||
records, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user