LogoPly
Features

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

MethodDescription
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
}