File upload
OpenAPI 3.0 provides two ways to upload files:
File upload via request body
File would be uploaded via request body.
The request body should be of type string
and format binary
.
- Spec
- Client
- Server
openapi: 3.0.3
info:
title: Example
version: 1.0.0
paths:
/avatar:
post:
summary: Upload avatar
operationId: uploadAvatar
requestBody:
required: true
content:
image/png:
schema:
type: string
format: binary
responses:
'200':
description: OK
package example
import (
"context"
"os"
"github.com/ogen-go/ogen/example/api"
)
func UploadAvatarFile(ctx context.Context, client *api.Client, p string) error {
img, err := os.Open(p)
if err != nil {
return err
}
defer func() {
_ = img.Close()
}()
return client.UploadAvatar(ctx, api.UploadAvatarReq{Data: img})
}
package example
import (
"context"
"image/png"
"io"
"os"
"github.com/go-faster/errors"
api "<your_api_package>"
)
var _ api.Handler = AvatarService{}
type Storage interface {
Store(ctx context.Context, data io.Reader) error
}
type AvatarService struct {
storage Storage
}
func (s AvatarService) UploadAvatar(ctx context.Context, req api.UploadAvatarReq) error {
f, err := os.CreateTemp("", "avatar")
if err != nil {
return resp, err
}
defer func() {
_ = f.Close()
}()
const maxImageBytes = 16 * 1024 * 1024 // 16MB
if _, err := io.Copy(f, io.LimitReader(req.Data, maxImageBytes)); err != nil {
return resp, err
}
// Sniff image format and size.
header, err := png.DecodeConfig(f)
if err != nil {
return resp, errors.Wrap(err, "decode image")
}
// Validate image size.
if header.Width < 96 || header.Height < 96 {
return resp, errors.New("image is too small")
}
if header.Width > 1000 || header.Height > 1000 {
return resp, errors.New("image is too big")
}
// Seek to the beginning of the file.
if _, err := f.Seek(0, io.SeekStart); err != nil {
return resp, err
}
// Store image.
if err := s.storage.Store(ctx, f); err != nil {
return resp, err
}
return resp, nil
}
File upload via multipart
request
Multiple files can be uploaded via multipart
request, along with other fields.
- Spec
- Client
- Server
openapi: 3.0.3
info:
title: Example
version: 1.0.0
paths:
/post:
post:
summary: Creates new post
operationId: newPost
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [ title, content ]
properties:
title:
type: string
content:
type: string
thumbnail:
type: string
format: binary
attachments:
type: array
items:
type: string
format: binary
responses:
'200':
description: OK
package example
import (
"context"
"fmt"
"io"
"github.com/ogen-go/ogen/example/api"
ht "github.com/ogen-go/ogen/http"
)
type Post struct {
Title string
Content string
Thumbnail io.Reader
Attachments []io.Reader
}
func MakePost(ctx context.Context, client *api.Client, post Post) error {
req := &api.NewPostReqForm{
Title: post.Title,
Content: post.Content,
}
if t := post.Thumbnail; t != nil {
req.Thumbnail = api.NewOptMultipartFile(ht.MultipartFile{
Name: "thumbnail",
File: t,
})
}
for i, attachment := range post.Attachments {
req.Attachments = append(req.Attachments, ht.MultipartFile{
Name: fmt.Sprintf("attachment%d", i),
File: attachment,
})
}
return client.NewPost(ctx, req)
}
package example
import (
"context"
"strings"
"github.com/go-faster/errors"
"github.com/ogen-go/ogen/example/api"
)
var _ api.Handler = PostService{}
type PostService struct{}
func (s PostService) NewPost(ctx context.Context, req *api.NewPostReqForm) error {
if _, ok := req.Thumbnail.Get(); ok {
// Access optional file field.
}
// Iterate over files.
for _, attachment := range req.Attachments {
// Checking file name.
if !strings.HasPrefix(attachment.Name, "attachment") {
// Access array of files.
return errors.Errorf("invalid file name: %q", attachment.Name)
}
}
return nil
}