Go + Cloud2Pdf

Integração completa para geração de PDFs em Golang

🚀 Fast & Simple ⚡ Goroutines 📦 net/http

🚀 Quick Start

1

Inicializar Módulo

go mod init myapp go mod tidy
2

Criar Package cloud2pdf

Copie o código da seção abaixo

3

Usar no main.go

client := cloud2pdf.NewClient("seu_token_aqui") result, err := client.GenerateAsync("<h1>Hello!</h1>", "ref-123")

📦 Package cloud2pdf

Crie cloud2pdf/client.go

package cloud2pdf import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" ) const defaultAPIURL = "https://api.cloud2pdf.com/v1" type Client struct { APIUrl string APIToken string HTTPClient *http.Client } type GenerateAsyncRequest struct { HTML string `json:"html"` IsDisk bool `json:"is_disk"` Reference string `json:"reference,omitempty"` Options map[string]interface{} `json:"options"` } type GenerateAsyncResponse struct { PdfID int `json:"pdf_id"` JobID string `json:"job_id"` Status string `json:"status"` Reference string `json:"reference"` } type StatusResponse struct { Status string `json:"status"` PdfID int `json:"pdf_id"` PublicLink map[string]interface{} `json:"public_link,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` Error string `json:"error,omitempty"` } func NewClient(apiToken string) *Client { return &Client{ APIUrl: defaultAPIURL, APIToken: apiToken, HTTPClient: &http.Client{ Timeout: 30 * time.Second, }, } } func (c *Client) GenerateAsync(html, reference string, options ...map[string]interface{}) (*GenerateAsyncResponse, error) { opts := map[string]interface{}{ "format": "A4", "landscape": false, } if len(options) > 0 { for k, v := range options[0] { opts[k] = v } } req := GenerateAsyncRequest{ HTML: html, IsDisk: true, Reference: reference, Options: opts, } body, err := json.Marshal(req) if err != nil { return nil, fmt.Errorf("failed to marshal request: %w", err) } httpReq, err := http.NewRequest("POST", c.APIUrl+"/pdf/generate-async", bytes.NewBuffer(body)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Authorization", "Bearer "+c.APIToken) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Accept", "application/json") resp, err := c.HTTPClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusAccepted { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var result GenerateAsyncResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } return &result, nil } func (c *Client) CheckStatus(pdfID int) (*StatusResponse, error) { url := fmt.Sprintf("%s/pdf/status/%d", c.APIUrl, pdfID) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "Bearer "+c.APIToken) req.Header.Set("Accept", "application/json") resp, err := c.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var result StatusResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } return &result, nil } func (c *Client) WaitForCompletion(pdfID int, maxAttempts int, delay time.Duration) (*StatusResponse, error) { for i := 0; i < maxAttempts; i++ { status, err := c.CheckStatus(pdfID) if err != nil { return nil, err } if status.Status == "completed" { return status, nil } if status.Status == "failed" { return nil, fmt.Errorf("PDF generation failed: %s", status.Error) } time.Sleep(delay) } return nil, fmt.Errorf("PDF generation timeout") } func (c *Client) Download(pdfID int) ([]byte, error) { url := fmt.Sprintf("%s/pdf/download/%d", c.APIUrl, pdfID) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "Bearer "+c.APIToken) resp, err := c.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } return io.ReadAll(resp.Body) } func (c *Client) DownloadAndSave(pdfID int, filename string) error { content, err := c.Download(pdfID) if err != nil { return err } return os.WriteFile(filename, content, 0644) }

🎮 Exemplo Completo

package main import ( "fmt" "log" "time" "myapp/cloud2pdf" ) func main() { // Criar cliente client := cloud2pdf.NewClient("seu_token_aqui") // HTML para converter html := ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> body { font-family: Arial, sans-serif; padding: 20px; } h1 { color: #004aad; } </style> </head> <body> <h1>Invoice #12345</h1> <p>Customer: John Doe</p> <p>Amount: $1,234.56</p> </body> </html> ` // Gerar PDF assíncrono fmt.Println("Generating PDF...") result, err := client.GenerateAsync(html, "invoice-12345", map[string]interface{}{ "format": "A4", "landscape": false, "margins": map[string]string{ "top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm", }, }) if err != nil { log.Fatal(err) } fmt.Printf("PDF ID: %d, Status: %s\n", result.PdfID, result.Status) // Aguardar conclusão fmt.Println("Waiting for completion...") completed, err := client.WaitForCompletion(result.PdfID, 60, 5*time.Second) if err != nil { log.Fatal(err) } fmt.Printf("PDF Ready! Status: %s\n", completed.Status) if publicLink, ok := completed.PublicLink["url"].(string); ok { fmt.Printf("Public URL: %s\n", publicLink) } // Download do PDF fmt.Println("Downloading PDF...") err = client.DownloadAndSave(result.PdfID, "invoice-12345.pdf") if err != nil { log.Fatal(err) } fmt.Println("PDF saved successfully!") }

🌐 HTTP Handler (API Endpoint)

package main import ( "encoding/json" "fmt" "log" "net/http" "time" "myapp/cloud2pdf" ) var pdfClient *cloud2pdf.Client func init() { pdfClient = cloud2pdf.NewClient("seu_token_aqui") } type GenerateRequest struct { InvoiceID int `json:"invoice_id"` HTML string `json:"html"` } type GenerateResponse struct { PdfID int `json:"pdf_id"` Status string `json:"status"` StatusURL string `json:"status_url"` } func generatePDFHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req GenerateRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } // Gerar PDF assíncrono result, err := pdfClient.GenerateAsync( req.HTML, fmt.Sprintf("invoice-%d", req.InvoiceID), ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Salvar PDF ID no banco (exemplo) // db.SavePdfID(req.InvoiceID, result.PdfID) // Retornar resposta response := GenerateResponse{ PdfID: result.PdfID, Status: result.Status, StatusURL: fmt.Sprintf("/pdf/status/%d", result.PdfID), } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } func checkStatusHandler(w http.ResponseWriter, r *http.Request) { // Extrair PDF ID da URL var pdfID int fmt.Sscanf(r.URL.Path, "/pdf/status/%d", &pdfID) status, err := pdfClient.CheckStatus(pdfID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(status) } func downloadPDFHandler(w http.ResponseWriter, r *http.Request) { var pdfID int fmt.Sscanf(r.URL.Path, "/pdf/download/%d", &pdfID) content, err := pdfClient.Download(pdfID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/pdf") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=document-%d.pdf", pdfID)) w.Write(content) } func main() { http.HandleFunc("/pdf/generate", generatePDFHandler) http.HandleFunc("/pdf/status/", checkStatusHandler) http.HandleFunc("/pdf/download/", downloadPDFHandler) log.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }

⚡ Goroutines para Processamento Assíncrono

package main import ( "fmt" "log" "sync" "time" "myapp/cloud2pdf" ) type PDFJob struct { ID int HTML string Reference string } func processPDFJob(client *cloud2pdf.Client, job PDFJob, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Processing job %d...\n", job.ID) // Gerar PDF result, err := client.GenerateAsync(job.HTML, job.Reference) if err != nil { log.Printf("Job %d failed: %v\n", job.ID, err) return } // Aguardar conclusão completed, err := client.WaitForCompletion(result.PdfID, 60, 5*time.Second) if err != nil { log.Printf("Job %d timeout: %v\n", job.ID, err) return } fmt.Printf("Job %d completed! PDF ID: %d\n", job.ID, result.PdfID) // Salvar no banco ou processar // db.UpdatePDFStatus(job.ID, completed) } func main() { client := cloud2pdf.NewClient("seu_token_aqui") // Lista de jobs jobs := []PDFJob{ {ID: 1, HTML: "<h1>Invoice 1</h1>", Reference: "inv-1"}, {ID: 2, HTML: "<h1>Invoice 2</h1>", Reference: "inv-2"}, {ID: 3, HTML: "<h1>Invoice 3</h1>", Reference: "inv-3"}, } var wg sync.WaitGroup // Processar jobs em paralelo for _, job := range jobs { wg.Add(1) go processPDFJob(client, job, &wg) } // Aguardar todos os jobs wg.Wait() fmt.Println("All jobs completed!") }

💡 Dicas

✅

Use Goroutines para Paralelismo

Processe múltiplos PDFs simultaneamente

✅

Context para Timeouts

Use context.Context para controle de timeouts

✅

Error Handling

Sempre verifique erros retornados

⚡

Pool de Workers

Use worker pools para controlar concorrência