前提・実現したいこと
ローカル環境で動く画像投稿機能を作成しています。
具体的には、以下のプロセスで画像をS3にアップロードしたいです。
- ユーザーが画面上から画像をアップロード(複数画像可能)
- 投稿ボタンを押すと、画像ファイルがサーバーに渡される
- サーバーで画像がS3に渡される処理が実行される
現在問題として、「3. サーバーで画像がS3に渡される処理が実行される」時点でnilエラーが発生しています。
フロントエンドにはReact、バックエンドサーバーにはGolangを利用しています。
フロントからサーバーへの通信にはaxios/ginをそれぞれ用いています。
また、画像アップロードのライブラリとしてdropzoneを使っています。
発生している問題・エラーメッセージ
client
1POST http://localhost:4000/api/post 500 (Internal Server Error) 2createError.js:17 Uncaught (in promise) Error: Request failed with status code 500 3 at createError (createError.js:17) 4 at settle (settle.js:19) 5 at XMLHttpRequest.handleLoad (xhr.js:60)
server
1[GIN] 2019/07/18 - 08:47:34 | 204 | 33.322µs | ::1 | OPTIONS /api/post 2 32019/07/18 08:47:34 [Recovery] 2019/07/18 - 08:47:34 panic recovered: 4POST /api/post HTTP/1.1 5Host: localhost:4000 6Accept: application/json, text/plain, */* 7Accept-Encoding: gzip, deflate, br 8Accept-Language: en-US,en;q=0.9 9Connection: keep-alive 10Content-Length: 68 11Content-Type: application/json;charset=UTF-8 12Origin: http://localhost:3000 13Referer: http://localhost:3000/post/finish 14User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 15 16 17runtime error: invalid memory address or nil pointer dereference 18/usr/local/go/src/runtime/panic.go:82 (0x10426f0) 19 panicmem: panic(memoryError) 20/usr/local/go/src/runtime/signal_unix.go:390 (0x104251f) 21 sigpanic: panicmem() 22/Users/jpskgc/go/src/github.com/gin-gonic/gin/context.go:532 (0x17122f5) 23 (*Context).MultipartForm: return c.Request.MultipartForm, err 24/Users/jpskgc/go/src/github.com/gin-gonic/gin/context.go:147 (0x14fdea9) 25 (*Context).Next: c.handlers[c.index](c) 26/Users/jpskgc/go/src/github.com/gin-gonic/gin/recovery.go:83 (0x1511939) 27 RecoveryWithWriter.func1: c.Next() 28/Users/jpskgc/go/src/github.com/gin-gonic/gin/context.go:147 (0x14fdea9) 29 (*Context).Next: c.handlers[c.index](c) 30/Users/jpskgc/go/src/github.com/gin-gonic/gin/logger.go:240 (0x15109e0) 31 LoggerWithConfig.func1: c.Next() 32/Users/jpskgc/go/src/github.com/gin-gonic/gin/context.go:147 (0x14fdea9) 33 (*Context).Next: c.handlers[c.index](c) 34/Users/jpskgc/go/src/github.com/gin-gonic/gin/gin.go:391 (0x1507da9) 35 (*Engine).handleHTTPRequest: c.Next() 36/Users/jpskgc/go/src/github.com/gin-gonic/gin/gin.go:352 (0x150749d) 37 (*Engine).ServeHTTP: engine.handleHTTPRequest(c) 38/usr/local/go/src/net/http/server.go:2774 (0x12e2207) 39 serverHandler.ServeHTTP: handler.ServeHTTP(rw, req) 40/usr/local/go/src/net/http/server.go:1878 (0x12dddf0) 41 (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req) 42/usr/local/go/src/runtime/asm_amd64.s:1337 (0x1059ed0) 43 goexit: BYTE $0x90 // NOP 44 45[GIN] 2019/07/18 - 08:47:34 | 500 | 119.560492ms | ::1 | POST /api/post
該当のソースコード
React
1//import 2 3interface ArticleState { 4 title: string; 5 content: string; 6 redirect: boolean; 7 files: File[]; 8} 9 10class Post extends React.Component<{}, ArticleState> { 11 constructor(props: {}) { 12 super(props); 13 this.state = { 14 title: '', 15 content: '', 16 redirect: false, 17 files: [], 18 }; 19 this.handleChangeTitle = this.handleChangeTitle.bind(this); 20 this.handleChangeContent = this.handleChangeContent.bind(this); 21 this.handleSubmit = this.handleSubmit.bind(this); 22 this.renderRedirect = this.renderRedirect.bind(this); 23 this.handleOnDrop = this.handleOnDrop.bind(this); 24 } 25 26 handleOnDrop(acceptedFiles: File[]) { 27 this.setState({files: this.state.files.concat(acceptedFiles)}); 28 } 29 30 handleChangeTitle(e: React.FormEvent<HTMLInputElement>) { 31 this.setState({title: e.currentTarget.value}); 32 } 33 34 handleChangeContent(e: React.FormEvent<HTMLInputElement>) { 35 this.setState({content: e.currentTarget.value}); 36 } 37 38 handleSubmit() { 39 this.setState({ 40 redirect: true, 41 }); 42 const data = { 43 title: this.state.title, 44 content: this.state.content, 45 files: this.state.files, 46 }; 47 axios.post('http://localhost:4000/api/post', data).then(res => { 48 console.log(res); 49 }); 50 } 51 52 renderRedirect = () => { 53 if (this.state.redirect) { 54 return <Redirect to="/post/finish" />; 55 } 56 }; 57 58 render() { 59 return ( 60 <Container text style={{marginTop: '3em'}}> 61 <Form> 62 <Form.Input 63 label="Title" 64 placeholder="" 65 name="title" 66 value={this.state.title} 67 onChange={this.handleChangeTitle} 68 /> 69 <Form.Field 70 label="Content" 71 placeholder="" 72 name="content" 73 value={this.state.content} 74 rows="20" 75 control="textarea" 76 onChange={this.handleChangeContent} 77 /> 78 {this.renderRedirect()} 79 <input type="file" id="file" hidden /> */} 80 <Dropzone accept="image/*" onDrop={this.handleOnDrop}> 81 {({getRootProps, getInputProps, open}) => ( 82 <section> 83 <div {...getRootProps()} style={{margin: '20px auto'}}> 84 <input {...getInputProps()} /> 85 <p>Drag 'n' drop some files here, or click to select files</p> 86 <button type="button" onClick={open}> 87 Open File Dialog 88 </button> 89 </div> 90 </section> 91 )} 92 </Dropzone> 93 <Form.Button content="Submit" onClick={this.handleSubmit} /> 94 </Form> 95 </Container> 96 ); 97 } 98} 99 100export default Post;
Golang
1package main 2 3//import 4 5type Article struct { 6 ID int `json:"id"` 7 TITLE string `json:"title"` 8 CONTENT string `json:"content"` 9} 10 11var articles []Article 12 13type Param struct { 14 Bucket string 15 Key string 16 Expires string 17 ContentType string 18} 19 20func main() { 21 22 awsAccessKeyID := "Insert Key Here" 23 awsSecretAccessKey := "Insert Secret Here" 24 token := "" 25 26 //mysql 27 db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/article") 28 if err != nil { 29 panic(err.Error()) 30 } 31 defer db.Close() 32 33 err = db.Ping() 34 if err != nil { 35 panic(err.Error()) 36 } 37 38 router := gin.Default() 39 40 router.Use(cors.New(cors.Config{ 41 AllowOrigins: []string{"*"}, 42 AllowMethods: []string{"GET", "POST", "OPTIONS"}, 43 AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"}, 44 ExposeHeaders: []string{"Content-Length"}, 45 AllowCredentials: true, 46 AllowOriginFunc: func(origin string) bool { 47 return true 48 }, 49 MaxAge: 15 * time.Second, 50 })) 51 52 api := router.Group("/api") 53 { 54 api.POST("/post", func(c *gin.Context) { 55 //mysql 56 var article Article 57 c.BindJSON(&article) 58 ins, err := db.Prepare("INSERT INTO articles(title,content) VALUES(?,?)") 59 if err != nil { 60 log.Fatal(err) 61 } 62 ins.Exec(article.TITLE, article.CONTENT) 63 64 //aws 65 creds := credentials.NewStaticCredentials(awsAccessKeyID, awsSecretAccessKey, token) 66 67 cfg := aws.NewConfig().WithRegion("ap-northeast-1").WithCredentials(creds) 68 svc := s3.New(session.New(), cfg) 69 70 //ここでformがnilになって落ちてしまいます 71 form, _ := c.MultipartForm() 72 files := form.File["upload[]"] 73 74 for _, file := range files { 75 f, err := file.Open() 76 77 defer f.Close() 78 fileInfo, _ := f.(*os.File).Stat() 79 size := fileInfo.Size() 80 buffer := make([]byte, size) 81 82 f.Read(buffer) 83 fileBytes := bytes.NewReader(buffer) 84 fileType := http.DetectContentType(buffer) 85 path := "/media/" + f.(*os.File).Name() 86 params := &s3.PutObjectInput{ 87 Bucket: aws.String("bucketname"), 88 Key: aws.String(path), 89 Body: fileBytes, 90 ContentLength: aws.Int64(size), 91 ContentType: aws.String(fileType), 92 } 93 resp, err := svc.PutObject(params) 94 95 fmt.Printf("response %s", awsutil.StringValue(resp)) 96 } 97 98 c.JSON(http.StatusOK, gin.H{"status": "ok"}) 99 100 }) 101 } 102 103 router.Run(":4000") 104}
試したこと
デバッグしたところ、サーバーサイドのメソッドでnilが原因で落ちていることがわかりました
form, _ := c.MultipartForm()
以上、お手数ですがよろしくお願いいたします。

回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。