package web import ( "bytes" "encoding/base64" "io" "mime/multipart" "net/http" p "github.com/ajswis/go-pkparse-server/pokemon" pokemonparsing "github.com/ajswis/go-pkparse-server/pokemon-parsing" "github.com/gin-gonic/gin" ) /* File uploads: curl -X POST http://localhost:8080/parse \ -H "Content-Type: multipart/form-data" \ --form "pokemon=@/path/to/test/file1" \ --form "pokemon=@/path/to/test/file2" JSON POST: curl -X POST http://localhost:8080/parse \ -H "Content-Type: application/json" \ -d '{"pokemon": ["base64_encoded_pokemon_file", "another_base64_encoded_pokemon_file"]}' Form POST: curl -X POST http://localhost:8080/parse \ -H "Content-Type: multipart/form-data" \ --form 'pokemon=base64_encoded_pokemon_file' \ --form 'pokemon=another_base64_encoded_pokemon_file' Form POST + File upload: curl -X POST http://localhost:8080/parse \ -H "Content-Type: multipart/form-data" \ --form "pokemon=@/path/to/test/file1" \ --form "pokemon=@/path/to/test/file2" \ --form 'pokemon=base64_encoded_pokemon_file' \ */ type pokemonParseRequest struct { Pokemon []string `json:"pokemon" form:"pokemon"` } // parse grabs multipart file uploads under the key `pokemon`, aggregates them, // and delegates work to the parser. func (s *Server) parse(c *gin.Context) { var rawPokemon []p.RawPokemon // If the POST is `multipart/form-data`, check for file uploads and read them if c.ContentType() == "multipart/form-data" { fileHeaders, err := uploadedPokemonFiles(c) if err != nil { render(c, http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if len(fileHeaders) > 0 { err = pokemonFromFileUpload(fileHeaders, &rawPokemon) if err != nil { render(c, http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } } // For non-file-upload form submissions or all other `Content-Type`s err := pokemonFromEncodedUpload(c, &rawPokemon) if err != nil { render(c, http.StatusInternalServerError, gin.H{"error": err.Error()}) return } pkmn, err := pokemonparsing.ParseAll(rawPokemon) if err != nil { render(c, http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } render(c, http.StatusOK, pkmn) } // uploadedPokemonFiles extracts any existing FileHeaders from file uploads func uploadedPokemonFiles(c *gin.Context) ([]*multipart.FileHeader, error) { form, err := c.MultipartForm() if err != nil { return nil, err } return form.File["pokemon"], nil } // pokemonFromFileUpload reads data from uploaded files and returns the raw // pokemon func pokemonFromFileUpload(files []*multipart.FileHeader, rawPokemon *[]p.RawPokemon) error { for _, file := range files { var err error var src multipart.File src, err = file.Open() if err != nil { return err } defer src.Close() buf := bytes.NewBuffer(nil) if _, err = io.Copy(buf, src); err != nil { return err } *rawPokemon = append(*rawPokemon, buf.Bytes()) } return nil } // pokemonFromEncodedUpload binds the POST data to a `pokemonParseRequest` and // converts each base64 string to a `[]byte` slice. func pokemonFromEncodedUpload(c *gin.Context, rawPokemon *[]p.RawPokemon) error { var request pokemonParseRequest if err := c.ShouldBind(&request); err != nil { return err } for _, pkmn := range request.Pokemon { var err error var decoded []byte decoded, err = base64.StdEncoding.DecodeString(pkmn) if err != nil { return err } *rawPokemon = append(*rawPokemon, decoded) } return nil }