File server
The file-server example demonstrates a file-upload endpoint: how the spec’s
type: file maps onto the generated server and client, and how the runtime
surfaces the uploaded file to your handler.
Tip
Source: file-server/.
Build the server under restapi/cmd/file-upload-server, then run the client with
go run upload_file.go swagger.yml.
The spec
An upload is a multipart/form-data operation with a formData parameter of
type: file:
/upload:
post:
tags:
- uploads
summary: uploads
operationId: uploadFile
consumes:
- multipart/form-data
parameters:
- name: file
in: formData
type: file
required: trueFull source: file-server/swagger.yml
Server side
The generated handler receives the file as an io.ReadCloser on
params.File. At runtime it’s a *runtime.File, so a type assertion gives you
the multipart header — filename and size — before you stream the body to disk:
api.UploadsUploadFileHandler = uploads.UploadFileHandlerFunc(func(params uploads.UploadFileParams) middleware.Responder {
if params.File == nil {
return middleware.Error(http.StatusNotFound, stderrors.New("no file provided"))
}
defer func() {
_ = params.File.Close()
}()
if namedFile, ok := params.File.(*runtime.File); ok {
log.Printf("received file name: %s", namedFile.Header.Filename)
log.Printf("received file size: %d", namedFile.Header.Size)
}
// uploads file and save it locally
filename := path.Join(uploadFolder, fmt.Sprintf("uploaded_file_%d.dat", uploadCounter))
uploadCounter++
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
return middleware.Error(http.StatusInternalServerError, stderrors.New("could not create file on server"))
}
n, err := io.Copy(f, params.File)
if err != nil {
return middleware.Error(http.StatusInternalServerError, stderrors.New("could not upload file on server"))
}
log.Printf("copied bytes %d", n)
log.Printf("file uploaded copied as %s", filename)
return uploads.NewUploadFileOK()
})Full source: file-server/restapi/configure_file_upload.go
Note the defer params.File.Close() and the io.Copy into a fresh file — the
handler owns the stream and is responsible for draining and closing it.
Client side
On the client, a file argument is a runtime.NamedReadCloser — an
io.ReadCloser that also reports a Name(). A plain *os.File satisfies it, so
you open the file and pass it straight to the generated parameter builder:
func upload(reader runtime.NamedReadCloser) error {
config := client.DefaultTransportConfig().WithHost("localhost:8000")
uploader := client.NewHTTPClientWithConfig(nil, config)
params := uploads.NewUploadFileParams().WithFile(reader)
_, err := uploader.Uploads.UploadFile(params)
return err
}Full source: file-server/upload_file.go
The generated UploadFile client method handles the multipart encoding; you only
supply the reader.
Related
- Streaming request/response bodies instead of a one-shot upload? See Streaming.
- Hand-wiring multipart without codegen? See the go-openapi/runtime examples.