package facepp import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "io" "mime/multipart" "net/http" "net/url" "os" "path/filepath" "strings" ) func (c *Client) SkinAnalyze(opts SkinAnalyzeOptions) (*SkinAnalyzeResponse, error) { if opts.ImageFile != "" { return c.skinAnalyzeFile(opts) } values := url.Values{} switch { case opts.ImageBase64 != "": values.Set("image_base64", opts.ImageBase64) case opts.ImageURL != "": values.Set("image_url", opts.ImageURL) default: return nil, errors.New("facepp: image_url, image_file or image_base64 is required") } applyOptionalParams(values, opts) return decodeSkinAnalyzeResult(c.postForm(values)) } func (c *Client) SkinAnalyzeFromFile(path string, opts ...SkinAnalyzeOptions) (*SkinAnalyzeResponse, error) { raw, err := os.ReadFile(path) if err != nil { return nil, err } req := SkinAnalyzeOptions{} if len(opts) > 0 { req = opts[0] } req.ImageBase64 = base64.StdEncoding.EncodeToString(raw) req.ImageFile = "" req.ImageURL = "" return c.SkinAnalyze(req) } func (c *Client) skinAnalyzeFile(opts SkinAnalyzeOptions) (*SkinAnalyzeResponse, error) { if c.apiKey == "" || c.apiSecret == "" { return nil, errors.New("facepp: api_key or api_secret is empty") } file, err := os.Open(opts.ImageFile) if err != nil { return nil, err } defer file.Close() var buf bytes.Buffer writer := multipart.NewWriter(&buf) if err := writer.WriteField("api_key", c.apiKey); err != nil { return nil, err } if err := writer.WriteField("api_secret", c.apiSecret); err != nil { return nil, err } if err := writeOptionalFields(writer, opts); err != nil { return nil, err } part, err := writer.CreateFormFile("image_file", filepath.Base(opts.ImageFile)) if err != nil { return nil, err } if _, err := io.Copy(part, file); err != nil { return nil, err } if err := writer.Close(); err != nil { return nil, err } req, err := http.NewRequest(http.MethodPost, c.endpoint, &buf) if err != nil { return nil, err } req.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if err := checkAPIError(body); err != nil { return decodeSkinAnalyzeResult(body, err) } if resp.StatusCode != http.StatusOK { return decodeSkinAnalyzeResult(body, fmt.Errorf("facepp: http %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))) } return decodeSkinAnalyzeResult(body, nil) } func applyOptionalParams(values url.Values, opts SkinAnalyzeOptions) { if opts.ReturnMaps != "" { values.Set("return_maps", opts.ReturnMaps) } if opts.ReturnMarks != "" { values.Set("return_marks", opts.ReturnMarks) } } func writeOptionalFields(writer *multipart.Writer, opts SkinAnalyzeOptions) error { if opts.ReturnMaps != "" { if err := writer.WriteField("return_maps", opts.ReturnMaps); err != nil { return err } } if opts.ReturnMarks != "" { return writer.WriteField("return_marks", opts.ReturnMarks) } return nil } func decodeSkinAnalyzeResult(body []byte, reqErr error) (*SkinAnalyzeResponse, error) { if len(body) == 0 { return nil, reqErr } resp, err := decodeSkinAnalyze(body) if err != nil { if reqErr != nil { return nil, reqErr } return nil, err } if reqErr != nil { return resp, reqErr } return resp, nil } func decodeSkinAnalyze(body []byte) (*SkinAnalyzeResponse, error) { var result SkinAnalyzeResponse if err := json.Unmarshal(body, &result); err != nil { return nil, err } return &result, nil }