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