gogo-echoweb3storage

Send uploaded file


I use echo framework for building REST API. I receive file via http request and i need to send it to the next API service by post request. How i can do this without storing file?

I've tried this way, but it doesn't feel right, because i have an error in response "Could not parse content as FormData"

func (h *Handler) UploadFile(c echo.Context) error {
    formFile, err := c.FormFile("file")
    if err != nil {
        return err
    }

    file, err := formFile.Open()
    if err != nil {
        return err
    }
    defer file.Close()

    cid, err := h.s.Upload(context.Background(), file)
    if err != nil {
        return err
    }

    return c.String(http.StatusOK, "Ok")

}

func (s *Storage) Upload(ctx context.Context, file io.Reader) (cid.Cid, error) {
    req, err := http.NewRequest(http.MethodPost, s.config.endpoint+"/upload", file)
    if err != nil {
        return cid.Undef, err
    }
    req.Header.Set("accept", "application/json")
    req.Header.Set("Content-Type", "multipart/form-data")
    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.config.token))
    res, err := s.config.hc.Do(req)
    if err != nil {
        return cid.Undef, err
    }
    d := json.NewDecoder(res.Body)
    if res.StatusCode != 200 {
        return cid.Undef, fmt.Errorf("unexpected response status: %d", res.StatusCode)
    }
    var out struct {
        Cid string `json:"cid"`
    }
    err = d.Decode(&out)
    if err != nil {
        return cid.Undef, err
    }
    return cid.Parse(out.Cid)
}

Solution

  • Here is the solution. Thanks Vlad Vladov.

    func (s *Storage) Upload(ctx context.Context, file multipart.File, fileHeader *multipart.FileHeader) (cid.Cid, error) {
        rCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
        defer cancel()
    
        buf := new(bytes.Buffer)
        writer := multipart.NewWriter(buf)
    
        part, err := writer.CreateFormFile("file", fileHeader.Filename)
        if err != nil {
            return cid.Undef, err
        }
    
        b, err := ioutil.ReadAll(file)
    
        if err != nil {
            return cid.Undef, err
        }
    
        part.Write(b)
        writer.Close()
        req, err := http.NewRequestWithContext(rCtx, http.MethodPost, s.config.endpoint+"/upload", buf)
        if err != nil {
            return cid.Undef, err
        }
    
        req.Header.Set("Content-Type", writer.FormDataContentType())
        req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.config.token))
        req.Header.Set("X-Client", "web3.storage/go")
    
        res, err := s.config.hc.Do(req)
        if err != nil {
            return cid.Undef, err
        }
    
        d := json.NewDecoder(res.Body)
        if res.StatusCode != 200 {
            var out responseError
            d.Decode(&out)
            log.Error(out)
            return cid.Undef, fmt.Errorf("unexpected response status: %d", res.StatusCode)
        }
        var out struct {
            Cid string `json:"cid"`
        }
        err = d.Decode(&out)
        if err != nil {
            return cid.Undef, err
        }
        return cid.Parse(out.Cid)
    }