LoginSignup
8
3

More than 3 years have passed since last update.

フロント(React)で受け取った画像ファイルをapi(Golang)に渡して最終的にS3に保存するまで

Posted at

概要

フロント:React、サーバーサイド:golangで記事投稿サイトを作っていました。
画像投稿機能の実装をまとめます。
web frameworkにはそれぞれ、axiosginを用いています。

実装

フロント(React)

post.tsx
interface ArticleState {
  files: File[];
}

class Post extends React.Component<{}, ArticleState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      files: [],
    };
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleOnDrop = this.handleOnDrop.bind(this);
  }


  handleOnDrop(acceptedFiles: File[]) {
    this.setState({files: this.state.files.concat(acceptedFiles)});
  }

  async handleSubmit() {
    const formData = new FormData();
    for (var i in this.state.files) {
      formData.append('images[]', this.state.files[i]);
    }

    const postArticleImage = postArticleImageFactory();
    const resImageNames = await postArticleImage(formData);
  }

  render() {
    return (
          <Dropzone accept="image/*" onDrop={this.handleOnDrop}>
          //省略
          <Form.Button content="Submit" onClick={this.handleSubmit} />
    );
  }
}
  • 画像投稿フォーム(ここではDropzone)から画像を投稿します。
  • 投稿ボタンがクリックされると、formDataに画像を詰めてapiにrequestを飛ばすメソッドを呼びます。
articleAPI.tsx
export const postArticleImageFactory = () => {
  const postArticleImage = async (formData: FormData) => {
    try {
      const response = await axios.post('/api/post/image', formData, {
        headers: {'Content-Type': 'multipart/form-data'},
      });

      if (response.status !== 200) {
        throw new Error('Server Error');
      }

      return response.data;
    } catch (err) {
      throw err;
    }
  };
  return postArticleImage;
};
  • headers: {'Content-Type': 'multipart/form-data'}を設定しないとapi通信でエラーが発生するので、設定しています。

バックエンド(Golang)

handler.go

    router := gin.Default()

    router.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"*"},
        AllowMethods:     []string{"GET", "POST", "OPTIONS"},
        AllowHeaders:     []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        AllowOriginFunc: func(origin string) bool {
            return true
        },
        MaxAge: 15 * time.Second,
    }))

    api := router.Group("/api")
    {
        api.POST("/post/image", func(c *gin.Context) {
            cntlr.PostImageController(c)
        })
    }
  • ginを利用してrequestを受け取っています。
service.go
func (s Service) PostImageService(c *gin.Context) []util.ImageName {

    form, _ := c.MultipartForm()
    files := form.File["images[]"]

    var imageNames []util.ImageName
    imageName := util.ImageName{}

    for _, file := range files {

        u, err := uuid.NewRandom()
        if err != nil {
            fmt.Println(err)
        }
        uu := u.String()

        s.dao.PostImageToS3Dao(file, uu)

        imageName.NAME = uu

        imageNames = append(imageNames, imageName)
    }

    return imageNames
}
  • gin.Contextから画像ファイルを取り出しています。
  • S3に保存する画像名を一意にする為、uuidを設定しています。(万が一画像名が同じだとバケットで上書きされる為)
s3.go
func (objs *S3) PostImageToS3(file *multipart.FileHeader, imageName string) error {
    creds := credentials.NewStaticCredentials(objs.APPID, objs.SECRET, "")

    cfg := aws.NewConfig().WithRegion("ap-northeast-1").WithCredentials(creds)
    svc := s3.New(session.New(), cfg)

    f, err := file.Open()

    if err != nil {
        log.Println(err)
    }

    defer f.Close()

    size := file.Size
    buffer := make([]byte, size)

    f.Read(buffer)
    fileBytes := bytes.NewReader(buffer)
    fileType := http.DetectContentType(buffer)
    path := "/media/" + imageName
    params := &s3.PutObjectInput{
        Bucket:        aws.String("<Bucket Name>"),
        Key:           aws.String(path),
        Body:          fileBytes,
        ContentLength: aws.Int64(size),
        ContentType:   aws.String(fileType),
    }
    resp, err := svc.PutObject(params)

    fmt.Printf("response %s", awsutil.StringValue(resp))

    return err
}
  • S3に保存するメソッドです。
  • テストしやすいようにS3に保存する目的だけのメソッドです(ゆえに返り値はerror)

ソースコード全貌は以下のgithubを参照してください
https://github.com/jpskgc/article

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3