File Upload
Read multipart files and save them to disk.
File Upload
Ply exposes the underlying fasthttp multipart API through FormFile,
MultipartForm, and SaveFile.
app.Post("/upload", func(c *core.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(400, map[string]string{"error": "missing file"})
return
}
name := filepath.Base(file.Filename)
dst := filepath.Join("uploads", name)
if err := os.MkdirAll("uploads", 0o755); err != nil {
c.JSON(500, map[string]string{"error": "cannot create upload directory"})
return
}
if err := c.SaveFile(file, dst); err != nil {
c.JSON(500, map[string]string{"error": "cannot save file"})
return
}
c.JSON(201, map[string]string{"saved": dst})
})Use a multipart form field named file in the client request.
Methods
| Method | Description |
|---|---|
FormFile(key) | Returns the first *multipart.FileHeader for a field |
MultipartForm() | Returns the parsed *multipart.Form |
SaveFile(header, dst) | Copies the uploaded file to dst |
The upload is parsed lazily on first access. This avoids multipart work for requests that never read a file.
SaveFile behavior
SaveFile opens the uploaded file, creates dst with os.Create, and copies
the bytes. It does not:
- create parent directories,
- sanitize the filename,
- enforce a size limit,
- validate the content type,
- choose a collision-free name.
Handle those in the handler or at the proxy.
Security
Uploaded names are client-controlled. Always reduce them to a safe base name:
name := filepath.Base(file.Filename)Do not pass file.Filename directly to filepath.Join. A value such as
../../app can escape the upload directory if the platform accepts it.
Validate before saving:
- maximum size, using
file.Size, - allowed extensions or detected content type,
- an authenticated user,
- a destination outside the web root.
Generate a new name when duplicates matter:
name := "upload-" + time.Now().UTC().Format("20060102T150405.000000000") +
filepath.Ext(file.Filename)Size limits
SaveFile does not limit the body. Set a request body limit in the reverse
proxy, or check file.Size before copying. A limit at the server layer is
still needed because the multipart body is parsed before the handler runs.
Multiple files
Use MultipartForm when a field contains several files:
form, err := c.MultipartForm()
if err != nil {
c.JSON(400, map[string]string{"error": "invalid multipart form"})
return
}
for _, header := range form.File["files"] {
// validate and save each header
}
Ply