软件工程

使用Pinata API上传文件

本文主要是介绍使用Pinata API上传文件,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

这是对Pinata 挑战的提交_

我建造的东西

我使用Golang和React构建了一个可以上传多个文件的工具,它可以上传多个到Pinata。

以下是它的功能分解。

  • 它检查请求是否使用POST方法。如果不是,它将发送一个状态码为405(方法不允许的方法)的错误响应并返回。
  • 它使用ParseMultipartForm函数解析请求中的多部分表单数据,最大文件大小为maxFileSize。如果解析表单数据时出错,它将发送一个状态码为400(错误请求)的错误响应并返回。
  • 它从请求中使用MultipartForm.File["files"]获取上传的文件列表。如果没有上传文件,它将发送一个状态码为400(错误请求)的错误响应并返回。
  • 它初始化了两个切片:responses用于存储成功的上传,errors用于存储上传过程中出现的任何错误。
  • 它创建了一个sync.WaitGroup和一个sync.Mutex以同步并发文件上传。
  • 它遍历每个上传的文件并为每个文件启动一个goroutine以使用uploadFileToPinata函数进行上传。wg.Add(1)增加了等待组计数器,在goroutine完成时使用defer wg.Done()减少计数器。
  • uploadFileToPinata函数将每个文件上传到Pinata API端点并返回响应及上传过程中出现的任何错误。
  • mu.Lock()和mu.Unlock()调用确保对responses和errors切片的更新是原子操作。
  • 所有的goroutines完成后,该函数使用wg.Wait()等待它们完成。
  • 它构造了一个JSON响应结构体result,包含成功的上传和错误。
  • 它将响应头的内容类型设置为JSON。
  • 它检查是否有错误。如果有错误,它将响应状态码设置为206(部分内容)。否则,将状态码设置为200(OK)。
  • 它将结果结构体编码为JSON并将其写入响应体。

总体来说,这段代码负责处理文件上传的过程,执行并行上传,并发送一个包含成功上传的文件以及过程中出现的任何错误信息的JSON响应。

演示版

前端展示 如图所示

Pinata上传的文件

我的代码

访问 Github 查看更多详情。

    package main

    import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "mime/multipart"
        "net/http"
        "os"
        "path/filepath"
        "sync"

        "github.com/joho/godotenv"
    )

    const (
        maxFileSize = 10 << 20 // 10 MB
    )

    type PinataResponse struct {
        IpfsHash  string `json:"IpfsHash"`
        PinSize   int    `json:"PinSize"`
        Timestamp string `json:"Timestamp"`
    }

    type ErrorResponse struct {
        Error string `json:"error"`
    }

    type Credentials struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }

    func main() {
        // 加载 .env 文件失败
        err := godotenv.Load(".env")
        if err != nil {
            log.Fatal("加载 .env 文件失败")
        }

        // http.HandleFunc("/upload", handleUpload)
        http.Handle("/upload", corsMiddleware(http.HandlerFunc(handleUpload)))
        fmt.Println("服务运行在 http://localhost:9000")
        log.Fatal(http.ListenAndServe(":9000", nil))
    }

    func corsMiddleware(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
            w.Header().Set("Access-Control-Allow-Headers", "Content-Type, pinata_api_key, pinata_secret_api_key")

            if r.Method == "OPTIONS" {
                w.WriteHeader(http.StatusNoContent)
                return
            }

            next.ServeHTTP(w, r)
        })
    }

    func handleUpload(w http.ResponseWriter, r *http.Request) {
        if r.Method != http.MethodPost {
            sendErrorResponse(w, "方法不允许", http.StatusMethodNotAllowed)
            return
        }

        err := r.ParseMultipartForm(maxFileSize)
        if err != nil {
            sendErrorResponse(w, "解析多部分表单错误: "+err.Error(), http.StatusBadRequest)
            return
        }

        files := r.MultipartForm.File["files"]
        if len(files) == 0 {
            sendErrorResponse(w, "没有上传任何文件", http.StatusBadRequest)
            return
        }

        responses := make([]PinataResponse, 0, len(files))
        errors := make([]string, 0)

        var wg sync.WaitGroup
        var mu sync.Mutex

        for _, fileHeader := range files {
            wg.Add(1)
            go func(fh *multipart.FileHeader) {
                defer wg.Done()

                response, err := uploadFileToPinata(fh)
                mu.Lock()
                defer mu.Unlock()

                if err != nil {
                    errors = append(errors, fmt.Sprintf("上传 %s 时出错: %v", fh.Filename, err))
                } else {
                    responses = append(responses, response)
                }
            }(fileHeader)
        }

        wg.Wait()

        result := struct {
            SuccessfulUploads []PinataResponse `json:"successful_uploads"`
            Errors            []string         `json:"errors,omitempty"`
        }{
            SuccessfulUploads: responses,
            Errors:            errors,
        }

        w.Header().Set("Content-Type", "application/json")
        if len(errors) > 0 {
            w.WriteHeader(http.StatusPartialContent)
        } else {
            w.WriteHeader(http.StatusOK)
        }
        json.NewEncoder(w).Encode(result)
    }

    func uploadFileToPinata(fileHeader *multipart.FileHeader) (PinataResponse, error) {
        file, err := fileHeader.Open()
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法打开文件: %w", err)
        }
        defer file.Close()

        var requestBody bytes.Buffer
        writer := multipart.NewWriter(&requestBody)

        part, err := writer.CreateFormFile("file", filepath.Base(fileHeader.Filename))
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法创建表单文件: %w", err)
        }

        _, err = io.Copy(part, file)
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法复制文件内容: %w", err)
        }

        err = writer.Close()
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法关闭多部分写入器: %w", err)
        }

        // 加载环境变量
        pinataAPIKey := os.Getenv("PINATA_API_KEY")
        pinataAPISecret := os.Getenv("PINATA_API_SECRET")
        pinataAPIURL := os.Getenv("PINATA_API_URL")

        req, err := http.NewRequest("POST", pinataAPIURL, &requestBody)
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法创建请求: %w", err)
        }

        req.Header.Set("Content-Type", writer.FormDataContentType())
        req.Header.Set("pinata_api_key", pinataAPIKey)
        req.Header.Set("pinata_secret_api_key", pinataAPISecret)

        client := &http.Client{}
        resp, err := client.Do(req)
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法发送请求: %w", err)
        }
        defer resp.Body.Close()

        if resp.StatusCode != http.StatusOK {
            return PinataResponse{}, fmt.Errorf("Pinata API 返回非 OK 状态: %s", resp.Status)
        }

        var pinataResp PinataResponse
        err = json.NewDecoder(resp.Body).Decode(&pinataResp)
        if err != nil {
            return PinataResponse{}, fmt.Errorf("无法解析Pinata响应: %w", err)
        }

        return pinataResp, nil
    }

    func sendErrorResponse(w http.ResponseWriter, message string, statusCode int) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(statusCode)
        json.NewEncoder(w).Encode(ErrorResponse{Error: message})
    }

全屏, 退出全屏

在共享的GitHub主页上找前端代码

了解更多详情
这篇关于使用Pinata API上传文件的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!