SDK Java per la Conversione dei File#

com.enconvert:enconvert-sdk è il client Java ufficiale per l'API EnConvert. Una sola dipendenza Maven o Gradle ti dà la conversione dei file (da URL a PDF, da DOCX a PDF, da HEIC a WebP, qualsiasi file in Markdown) più la superficie di web intelligence V2: perceive, discover, lookup, distill, ingest e watch. Richiede Java 17 o versioni successive, funziona sul java.net.http.HttpClient incluso nel JDK e porta con sé Gson come unica dipendenza di terze parti. Ogni chiamata è un semplice metodo bloccante che restituisce un record tipizzato, e le conversioni lunghe recuperano in modo trasparente dai timeout del reverse proxy effettuando il polling dello stato del job.

Maven Central: com.enconvert:enconvert-sdk:0.0.1 · Sorgente: conversionapi/java-sdk · Java: 17+ · Dipendenze: solo Gson

Installazione#

// build.gradle
dependencies {
    implementation 'com.enconvert:enconvert-sdk:0.0.1'
}
// build.gradle.kts
dependencies {
    implementation("com.enconvert:enconvert-sdk:0.0.1")
}
<!-- pom.xml -->
<dependency>
    <groupId>com.enconvert</groupId>
    <artifactId>enconvert-sdk</artifactId>
    <version>0.0.1</version>
</dependency>

L'HTTP è gestito da java.net.http.HttpClient del JDK. L'unico artefatto di terze parti che viene incluso è Gson per il JSON, dichiarato come dipendenza api in modo che sia visibile sul tuo classpath di compilazione.


Avvio rapido#

import com.enconvert.Enconvert;
import com.enconvert.model.ConversionResult;
import com.enconvert.model.UrlToPdfOptions;
import com.enconvert.model.v2.PerceiveOptions;
import com.enconvert.model.v2.PerceiveResult;

import java.util.List;

Enconvert client = new Enconvert(System.getenv("ENCONVERT_API_KEY"));

ConversionResult pdf = client.convertUrlToPdf("https://example.com",
        UrlToPdfOptions.builder().saveTo("page.pdf").build());
System.out.println(pdf.presignedUrl());

// Leggi una pagina come dovrebbe fare il tuo agente, con un punteggio di qualità allegato.
PerceiveResult page = client.v2.perceive("https://example.com",
        PerceiveOptions.builder().outputs(List.of("markdown", "structured")).build());
System.out.println(page.outputs().get("markdown").url());
System.out.println(page.renderQuality());   // ad es. 0.93

Ogni classe di opzioni è un builder immutabile e ogni risposta è un record Java, quindi gli accessori si leggono come pdf.presignedUrl() e page.renderQuality(). Il client mantiene un unico HttpClient condiviso e nessuno stato mutabile per richiesta, quindi una singola istanza può essere un singleton oppure un bean Spring condiviso tra i thread. Gli snippet qui sotto omettono gli import: le classi di opzioni e i tipi di risposta stanno in com.enconvert.model (conversione) e com.enconvert.model.v2 (web intelligence), le eccezioni in com.enconvert.exceptions.


Cosa espone il client#

Enconvert porta direttamente la superficie di conversione. La superficie di web intelligence vive sul campo pubblico final client.v2, un'istanza di EnconvertV2.

Gruppo Metodi Restituisce
URL singolo convertUrlToPdf, convertUrlToScreenshot, convertUrlToMarkdown ConversionResult
Upload di file convertImage, convertDocument, convertToMarkdown, convertToPdf ConversionResult
Intero sito convertWebsiteToPdf, convertWebsiteToScreenshot BatchSubmission
Stato getJobStatus, getBatchStatus, waitForBatch JobStatus, BatchStatus
v2 perceive perceive, perceiveDirect, getPerceiveOperation, perceiveBatch, getPerceiveBatch, downloadPerceiveArtifact PerceiveResult, PerceiveDirectResult, PerceiveBatchResult
v2 discover discover DiscoverResult
v2 lookup lookup LookupResult
v2 distill distill DistillResult
v2 ingest ingest, ingestFiles, getIngestJob, listIngestJobs, cancelIngestJob, retryIngestWebhook, getWebhookSecret, rotateWebhookSecret IngestJob, IngestJobList, WebhookRetryResult, WebhookSecret
v2 watch createWatcher, getWatcher, listWatchers, getWatcherSnapshots, updateWatcher, deleteWatcher Watcher, WatcherList, WatcherSnapshotList

La maggior parte dei metodi ha un overload breve senza argomento di opzioni, quindi sia client.v2.perceive(url) sia client.convertUrlToPdf(url) compilano. convertImage, distill e ingest sono le eccezioni: ognuno richiede sempre il proprio oggetto di opzioni, perché servono rispettivamente il formato di destinazione, lo schema e la sorgente.


Conversione dei file#

Gli endpoint di conversione coprono 43 coppie {input}-to-{output} implementate, due endpoint con rilevamento automatico (anything-to-markdown e anything-to-pdf) e gli endpoint di rendering nel browser. Il riferimento completo dei parametri si trova in Parametri e opzioni.

convertUrlToPdf#

Esegue il rendering di qualsiasi URL pubblico in PDF.

ConversionResult result = client.convertUrlToPdf("https://example.com",
        UrlToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder().pageSize("A4").orientation("landscape").build())
                .singlePage(false)
                .viewportWidth(1440)
                .saveTo("report.pdf")
                .build());
Opzione Tipo Predefinito Descrizione
saveTo String nessuno Percorso locale su cui scrivere il PDF. Le directory padre vengono create automaticamente.
singlePage boolean true true produce una singola pagina continua. false pagina utilizzando pdfOptions.pageSize.
pdfOptions PdfOptions nessuno Dimensione pagina, orientamento, margini, scala, scala di grigi, intestazione, piè di pagina. Vedi Opzioni PDF.
viewportWidth int 1920 Larghezza del viewport del browser in pixel.
viewportHeight int 1080 Altezza del viewport del browser in pixel.
loadMedia, enableScroll boolean true Attende immagini e video prima della cattura, e scorre dall'alto verso il basso per attivare i caricamenti lazy.
outputFilename String auto Sovrascrive il nome file generato.
auth, cookies, headers HttpBasicAuth, List<BrowserCookie>, Map<String, String> nessuno Credenziali, cookie iniettati e header di richiesta aggiuntivi per pagine protette da login.

convertUrlToScreenshot#

Cattura un PNG di qualsiasi URL. Le stesse opzioni di viewport, media, scroll, nome file, auth, cookie e header di convertUrlToPdf, meno singlePage e pdfOptions.

client.convertUrlToScreenshot("https://example.com",
        UrlToScreenshotOptions.builder().viewportWidth(1440).saveTo("screenshot.png").build());

convertUrlToMarkdown#

Estrae Markdown pulito in stile GitHub Flavored da un URL. Navigazione, footer, pubblicità e script vengono rimossi, il corpo principale dell'articolo viene mantenuto e viene anteposto un frontmatter YAML (titolo, descrizione, url, link, immagini). Stesso insieme di opzioni di convertUrlToScreenshot.

client.convertUrlToMarkdown("https://example.com/article",
        UrlToMarkdownOptions.builder().saveTo("article.md").build());

convertImage#

Converte tra jpeg, png, svg, heic e webp, oppure rasterizza un PDF in JPEG.

// Da un percorso su disco
client.convertImage(Path.of("photo.heic"),
        ConvertImageOptions.builder("webp").saveTo("photo.webp").build());

// Rasterizza un PDF
client.convertImage(Path.of("scan.pdf"),
        ConvertImageOptions.builder("jpeg").saveTo("scan.jpeg").build());

// Da byte in memoria con un nome file esplicito
byte[] bytes = Files.readAllBytes(Path.of("photo.heic"));
client.convertImage(new FileInput(bytes, "photo.heic"),
        ConvertImageOptions.builder("webp").build());

Su ogni metodo che accetta file esistono tre overload di input: java.nio.file.Path (lettura da disco), byte[] grezzi (il nome file diventa upload.bin) e com.enconvert.FileInput quando devi abbinare byte in memoria a un nome file reale. Il formato di input viene ricavato dall'estensione; il formato di output è obbligatorio.

Opzione Tipo Obbligatorio Descrizione
outputFormat String Passato a ConvertImageOptions.builder(outputFormat). Uno tra jpeg, png, svg, heic, webp. Gli alias come jpg vengono normalizzati.
saveTo String no Percorso locale su cui scrivere il risultato.
outputFilename String no Sovrascrive il nome file generato.

convertDocument#

Converte documenti e formati di dati strutturati. Il formato di output predefinito è pdf.

// da docx a pdf
client.convertDocument(Path.of("report.docx"),
        ConvertDocumentOptions.builder().saveTo("report.pdf").build());

// da json a yaml
client.convertDocument(Path.of("data.json"),
        ConvertDocumentOptions.builder().outputFormat("yaml").saveTo("data.yaml").build());

// da markdown a pdf con impostazioni di pagina
client.convertDocument(Path.of("README.md"),
        ConvertDocumentOptions.builder()
                .outputFormat("pdf")
                .pdfOptions(PdfOptions.builder()
                        .pageSize("A4")
                        .margins(new PdfMargins(20.0, 20.0, 25.0, 25.0))
                        .build())
                .saveTo("readme.pdf")
                .build());

Estensioni di input riconosciute: .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml.

EPUB non ha una coppia documentale dedicata. Invia invece i file .epub a convertToPdf oppure a convertToMarkdown.

Opzione Tipo Predefinito Descrizione
outputFormat String "pdf" Formato di destinazione.
saveTo String nessuno Percorso locale su cui scrivere il risultato.
outputFilename String nessuno Sovrascrive il nome file generato.
pdfOptions PdfOptions nessuno Impostazioni di pagina, rispettate quando l'output è PDF.

Conversioni supportate#

convertImage e convertDocument validano la coppia {input}-to-{output} rispetto agli endpoint che l'API implementa davvero. Una coppia non supportata genera subito IllegalArgumentException, elencando gli output validi per quell'input, invece di pagare un round trip per una richiesta che non può riuscire.

Input Output
json csv, toml, xml, yaml
xml csv, json
yaml json
csv json, xml
toml json
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp tra loro, tutte le 20 coppie
pdf jpeg

La stessa tabella è interrogabile a runtime tramite com.enconvert.Formats: Formats.validOutputsFor("json") restituisce [csv, toml, xml, yaml], Formats.validOutputsFor("pdf") restituisce [jpeg] e Formats.IMPLEMENTED_CONVERSIONS contiene tutti i 43 nomi di endpoint.

convertToMarkdown#

Invia qualsiasi documento supportato a un unico endpoint con rilevamento automatico e ottieni Markdown pulito. La gerarchia delle intestazioni sopravvive, il che rende questo passaggio un primo stadio naturale per una pipeline RAG.

client.convertToMarkdown(Path.of("handbook.docx"),
        ConvertToMarkdownOptions.builder().saveTo("handbook.md").build());

Accetta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, oltre ai formati office legacy e ODF. Il formato viene rilevato lato server, quindi non c'è alcun controllo dell'estensione lato client: qualsiasi file viene caricato così com'è. Le immagini non sono supportate e vengono rifiutate con 400. Le uniche opzioni sono saveTo e outputFilename.

convertToPdf#

L'altro endpoint con rilevamento automatico: quasi qualsiasi cosa in PDF.

// da pptx a pdf
client.convertToPdf(Path.of("slides.pptx"),
        ConvertToPdfOptions.builder().saveTo("slides.pdf").build());

// pdf in passthrough, convertito in scala di grigi
client.convertToPdf(Path.of("scan.pdf"),
        ConvertToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder().grayscale(true).build())
                .saveTo("scan-gray.pdf")
                .build());

Accetta office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, testo semplice, immagini raster, SVG, EPUB e un PDF esistente in passthrough. L'EPUB viene gestito qui perché non ha una coppia documentale dedicata. Le opzioni sono saveTo, outputFilename e pdfOptions.

Su questo endpoint viene rispettato solo grayscale. La geometria di pagina (dimensione pagina, larghezza e altezza, orientamento, margini, scala, intestazione, piè di pagina) viene ignorata da anything-to-pdf. Quando ti serve l'impostazione completa della pagina, passa invece da convertDocument oppure convertUrlToPdf.

Conversione di interi siti#

convertWebsiteToPdf e convertWebsiteToScreenshot individuano ogni pagina di un sito, convertono ciascuna in background e raccolgono i risultati in un unico ZIP. Entrambi sono asincroni e restituiscono un BatchSubmission. Entrambi richiedono una chiave API privata.

BatchSubmission batch = client.convertWebsiteToPdf("https://example.com",
        WebsiteToPdfOptions.builder()
                .crawlMode("sitemap")                      // "auto" (predefinito), "sitemap", "full"
                .excludePatterns(List.of("/blog/tag/"))    // solo in modalità full crawl
                .notificationEmail("[email protected]")
                .build());

System.out.println(batch.batchId() + " " + batch.urlCount() + " " + batch.discoveryMethod());

// Blocca finché il batch non esce da "processing", poi salva lo ZIP
BatchStatus status = client.waitForBatch(batch.batchId(),
        WaitForBatchOptions.builder().saveTo("site.zip").build());
System.out.println(status.completed() + " of " + status.total() + " pages converted");

convertWebsiteToScreenshot funziona in modo identico e produce uno ZIP di PNG. waitForBatch esegue il polling ogni 5 secondi per impostazione predefinita, si arrende dopo 30 minuti e accetta intervalMs, timeoutMs e saveTo. In caso di timeout genera ApiException con stato 504.

Eseguire il polling dello stato da soli#

JobStatus job = client.getJobStatus("job_abc123");
if ("success".equals(job.status())) System.out.println(job.presignedUrl());
if ("failed".equals(job.status())) System.err.println(job.error());
BatchStatus batch = client.getBatchStatus("bat_abc123");
if (!"processing".equals(batch.status())) System.out.println(batch.zipDownloadUrl());

Web intelligence (V2)#

Tutto ciò che sta sotto client.v2 trasforma le pagine web in dati pronti per gli agenti. Ogni lettura porta con sé renderQuality, un punteggio da 0.0 a 1.0 che dice quanto pulitamente la pagina è stata effettivamente renderizzata. Una pagina di challenge, un cookie wall o uno shell SPA vuoto tornano con un punteggio basso e con warnings() e deductions() popolati, invece di essere spacciati per contenuto reale, così una lettura difettosa non entra mai silenziosamente nel contesto del tuo agente. Il contenuto viene comunque restituito: viene solo segnalato. Le basi del modello si trovano nella panoramica V2.

Perceive#

Esegue il rendering di un URL negli artefatti che richiedi. Riferimento dell'endpoint: Perceive.

PerceiveResult page = client.v2.perceive("https://example.com",
        PerceiveOptions.builder()
                .outputs(List.of("markdown", "screenshot", "structured"))
                .extract(List.of("tables", "metadata"))
                .waitFor("css:main")
                .build());

System.out.println(page.renderQuality());                 // da 0.0 a 1.0
System.out.println(page.statusCode() + " " + page.deductions());  // ad es. 200 {http_error=0.7}
System.out.println(page.outputs().get("markdown").url()); // URL firmato, 15 minuti
System.out.println(page.structured());                    // forma definita dal chiamante
Opzione Tipo Predefinito Descrizione
outputs List<String> ["markdown", "structured"] Uno o più tra markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
extract List<String> nessuno Obiettivi euristici: tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
schema Map<String, Object> nessuno Schema JSON per l'estrazione strutturata.
waitFor, waitTimeoutMs String, int nessuno, 30000 Un selettore CSS, eventualmente con prefisso css:, oppure js:<expr> da attendere, con un budget da 0 a 60000 ms.
jsCode String nessuno JavaScript eseguito dopo la navigazione, massimo 20000 caratteri.
viewport, mobile PerceiveViewport, boolean 1920 x 1080, false Larghezza da 320 a 3840, altezza da 240 a 2160, oppure emulazione mobile.
onlyMainContent boolean true Rimuove navigazione, header, footer e banner dei cookie dall'artefatto Markdown e dall'estrazione main_content.
cacheMode String "enabled" enabled riutilizza una cache di 1 ora, bypass la salta, refresh forza un nuovo rendering.
blockResources List<String> nessuno Tipi di risorsa che il browser non deve caricare, per esempio image, font, script.
pdfOptions PdfOptions nessuno Ha effetto solo quando outputs contiene pdf.
headers, cookies, auth Map, List<BrowserCookie>, HttpBasicAuth nessuno Header di richiesta, cookie iniettati, credenziali HTTP Basic.
respectRobots boolean nessuno Rispetta le regole robots del sito.
Non ancora collegati. proxyUrl, geolocation e actionChain esistono sul builder ma non sono disponibili lato server e attualmente vengono rifiutati con 422.

Gli URL degli artefatti sono firmati per 15 minuti e vengono rifirmati a ogni lettura dell'operazione, quindi client.v2.getPerceiveOperation(page.operationId()) ti consegna link freschi. Puoi raggruppare fino a 1000 URL con un unico blocco di opzioni condiviso: i batch piccoli si completano inline, quelli più grandi tornano con stato queued, quindi vanno interrogati con il polling.

PerceiveBatchResult batch = client.v2.perceiveBatch(
        List.of("https://a.example.com", "https://b.example.com"),
        PerceiveBatchOptions.builder()
                .outputs(List.of("markdown"))
                .outputMode("zip")          // "manifest" (predefinito) oppure "zip"
                .build());

PerceiveBatchResult done = client.v2.getPerceiveBatch(batch.jobId());
System.out.println(done.completed() + "/" + done.total());

Salta del tutto il round trip dell'URL firmato con perceiveDirect, che restituisce in streaming i byte dell'artefatto. Richiede esattamente un output che produca artefatti (qualsiasi cosa tranne structured) e genera IllegalArgumentException prima dell'invio se ne chiedi di più o di meno:

PerceiveDirectResult direct = client.v2.perceiveDirect("https://example.com",
        PerceiveOptions.builder().outputs(List.of("pdf")).build());

Files.write(Path.of(direct.filename()), direct.content());
System.out.println(direct.renderQuality() + " " + direct.contentType());

// Riscarica un artefatto archiviato di un'operazione precedente
PerceiveDirectResult stored = client.v2.downloadPerceiveArtifact(page.operationId(), "markdown");

downloadPerceiveArtifact accetta un nome di output null oppure omesso quando l'operazione ha prodotto esattamente un artefatto, altrimenti restituisce 400 elencando gli output disponibili. Una volta che l'artefatto archiviato è scaduto, restituisce 410.

Discover#

Enumera gli URL di un sito senza renderizzare nulla. Non è coinvolto alcun browser, quindi è veloce. Riferimento dell'endpoint: Discover.

DiscoverResult found = client.v2.discover("https://example.com",
        DiscoverOptions.builder()
                .mode("hybrid")                        // "sitemap", "crawl", "hybrid"
                .maxUrls(200)
                .maxDepth(3)
                .excludePatterns(List.of("/tag/"))
                .build());

System.out.println(found.total() + " urls, truncated=" + found.truncated());
found.urls().forEach(System.out::println);
Opzione Tipo Predefinito Intervallo
mode String "hybrid" sitemap, crawl, hybrid
maxUrls int 100 da 1 a 1000
maxDepth int 2 da 1 a 5
includePatterns, excludePatterns List<String> nessuno Allowlist e denylist regex, massimo 50 ciascuna. La denylist viene applicata per seconda.
sameDomainOnly, respectRobots boolean true, nessuno Resta sul dominio seed, e rispetta le regole robots del sito.

Lookup#

Ricerca web categorizzata, con rendering automatico opzionale dei risultati migliori. Riferimento dell'endpoint: Lookup.

LookupResult search = client.v2.lookup("best static site generators",
        LookupOptions.builder()
                .category("web")        // web, news, images, scholar, patents, maps
                .numResults(10)
                .country("us")
                .timeFilter("month")    // hour, day, week, month, year
                .perceiveTop(3)         // renderizza automaticamente i 3 risultati migliori
                .build());

search.results().forEach(hit -> {
    System.out.println(hit.position() + " " + hit.title() + " " + hit.url());
    if (hit.perceive() != null) System.out.println("  quality " + hit.perceive().renderQuality());
});

Con perceiveTop maggiore di 0 (da 0 a 10, predefinito 0), gli URL dei primi N risultati vengono renderizzati tramite perceive e ogni risultato porta inline il suo PerceiveResult completo su hit.perceive(). numResults va da 1 a 100 e vale 10 per impostazione predefinita; page va da 1 a 10.

Distill#

Estrazione strutturata guidata da schema su una o più pagine. Riferimento dell'endpoint: Distill.

DistillResult extraction = client.v2.distill(
        DistillOptions.builder(Map.of("products", "list of product names with their listed price"))
                .urls(List.of("https://example.com/catalog"))
                .cssSchema(CssSchema.builder(".product-card", List.of(
                                CssField.builder("name", "text").selector("h3").build(),
                                CssField.builder("price", "text").selector(".price").build()))
                        .targetField("products")
                        .build())
                .build());

extraction.results().forEach(item ->
        System.out.println(item.data() + " via " + item.extractionTier()));

Il cssSchema opzionale esegue prima un passaggio CSS gratuito; solo i campi a cui non riesce a rispondere passano al livello LLM, e item.extractionTier() riporta quale percorso ha prodotto il record (css, llm, mixed oppure none). Puoi anche individuare prima gli URL invece di elencarli:

client.v2.distill(
        DistillOptions.builder(Map.of("title", "page title", "summary", "one-line summary"))
                .discoverFrom(new DistillDiscoverFrom("https://example.com", "sitemap", 10))
                .build());

Esattamente uno tra urls (massimo 50) e discoverFrom deve essere impostato, e schema è richiesto dalla factory del builder. Entrambe le regole vengono verificate lato client e generano IllegalArgumentException prima che parta qualsiasi richiesta.

Ingest#

Trasforma un intero sito, o un insieme di documenti caricati, in JSONL suddiviso in chunk e pronto per il RAG attraverso un'unica pipeline. Ingest è sempre asincrono. Riferimento dell'endpoint: Ingest.

// Da un sito
IngestJob job = client.v2.ingest(IngestOptions.builder()
        .mode("sitemap")                                  // "urls" (predefinito), "sitemap", "crawl"
        .url("https://docs.example.com")
        .maxPages(100)
        .chunk(new IngestChunkOptions(512, 1))            // maxWords, sentenceOverlap
        .webhookUrl("https://my.app/hooks/enconvert")
        .build());

// Oppure da file caricati
IngestJob fileJob = client.v2.ingestFiles(
        List.of(new FileInput(Files.readAllBytes(Path.of("handbook.pdf")), "handbook.pdf"),
                new FileInput(Files.readAllBytes(Path.of("notes.docx")), "notes.docx")),
        IngestFilesOptions.builder().chunk(new IngestChunkOptions(512, 1)).build());

// Esegui il polling per il JSONL
IngestJob status = client.v2.getIngestJob(job.jobId());
if ("completed".equals(status.status())) {
    System.out.println(status.outputUrl() + " (" + status.totalChunks() + " chunks)");
}

client.v2.listIngestJobs(V2ListOptions.builder().limit(20).build());
client.v2.cancelIngestJob(job.jobId());   // idempotente

ingestFiles accetta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, oltre ai formati office legacy e ODF. Il chunking usa per impostazione predefinita 512 parole (da 32 a 4000) con 1 frase di sovrapposizione (da 0 a 10). mode vale urls per impostazione predefinita, il che richiede un elenco urls non vuoto e vieta url; ogni altra modalità richiede un url seed e vieta urls. L'SDK verifica quell'abbinamento prima dell'invio.

I webhook di completamento sono firmati con HMAC. Recupera il secret e i nomi degli header che ti servono per verificare una consegna, ruotalo quando trapela e rispedisci una consegna che il tuo endpoint ha perso:

WebhookSecret secret = client.v2.getWebhookSecret();
System.out.println(secret.signatureHeader() + " " + secret.signatureScheme());

client.v2.rotateWebhookSecret();          // le vecchie firme smettono subito di essere valide

WebhookRetryResult retry = client.v2.retryIngestWebhook(job.jobId());
System.out.println(retry.delivered() + " after " + retry.attempts() + " attempts");

Watch#

Monitoraggio ricorrente delle modifiche su un URL, con notifica via email e webhook. Riferimento dell'endpoint: Watch.

Watcher watcher = client.v2.createWatcher("https://example.com/pricing",
        WatchCreateOptions.builder()
                .frequencyMinutes(60)      // da 60 a 43200, minimo orario
                .diffMode("auto")          // auto, text, structured, tables, metadata
                .webhookUrl("https://my.app/hooks/changes")
                .notifyEmail(true)
                .build());

WatcherSnapshotList history = client.v2.getWatcherSnapshots(watcher.watcherId(),
        SnapshotListOptions.builder().limit(10).build());
history.snapshots().forEach(s ->
        System.out.println(s.checkedAt() + " changed=" + s.hasChanges()
                + " similarity=" + s.similarity()));

client.v2.updateWatcher(watcher.watcherId(), WatcherUpdate.builder().status("paused").build());
client.v2.updateWatcher(watcher.watcherId(), WatcherUpdate.builder().webhookUrl("").build());
client.v2.deleteWatcher(watcher.watcherId());   // soft delete, idempotente

listWatchers() e getWatcher(watcherId) li rileggono. updateWatcher richiede almeno un campo e altrimenti genera IllegalArgumentException. Una stringa vuota esplicita per webhookUrl cancella il webhook, mentre lasciarlo null significa che non cambia nulla. deleteWatcher è una cancellazione soft: restituisce il watcher marcato come eliminato con stato deleted, e un watcher eliminato viene poi letto come 404.

I diff degli snapshot contengono contenuto di pagina non attendibile. WatcherSnapshot.changes() è testo grezzo prelevato dalla pagina monitorata. Effettuane l'escape prima di renderizzarlo in una dashboard, in un'email o in un messaggio di chat.

Opzioni PDF#

PdfOptions è condiviso da convertUrlToPdf, convertWebsiteToPdf, convertDocument, convertToPdf e PerceiveOptions.pdfOptions.

client.convertUrlToPdf("https://internal.example.com/report",
        UrlToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder()
                        .pageSize("A4")
                        .orientation("landscape")
                        .margins(new PdfMargins(10.0, 10.0, 15.0, 15.0))
                        .scale(0.9)
                        .header(new PdfHeaderFooter("Quarterly Report", 15.0))
                        .footer(new PdfHeaderFooter("Confidential", 12.0))
                        .build())
                .auth(new HttpBasicAuth("user", "pass"))
                .cookies(List.of(BrowserCookie.builder("session", "abc123").domain("internal.example.com").build()))
                .headers(Map.of("X-Tenant", "acme"))
                .saveTo("report.pdf")
                .build());
Campo Tipo Descrizione
pageSize String "A4", "A3", "Letter", "Legal" e simili.
pageWidth, pageHeight double Dimensioni personalizzate. Impostati insieme, hanno la precedenza su pageSize.
orientation String "portrait" oppure "landscape".
margins PdfMargins Record di top, bottom, left, right. Qualsiasi campo null viene omesso.
scale double Scala di rendering, ad esempio 0.9 per il 90 percento.
grayscale boolean Post-elabora il PDF in scala di grigi.
header, footer PdfHeaderFooter Record di content (massimo 2000 caratteri) e height.

BrowserCookie richiede un nome e un valore più domain oppure url; quando domain è impostato senza path, l'API imposta path a / per impostazione predefinita. Non combinare auth con un header Authorization esplicito, perché l'API rifiuta il conflitto.


Gestione degli errori#

Ogni eccezione dell'SDK estende EnconvertException, che a sua volta estende RuntimeException, quindi nulla impone una clausola throws sui tuoi punti di chiamata. Cattura prima le sottoclassi specifiche.

try {
    client.convertUrlToPdf("https://example.com");
} catch (AuthenticationException e) {
    System.err.println("Invalid or missing API key");
} catch (QuotaException e) {
    System.err.println("Request refused with 402: " + e.getMessage());
} catch (RateLimitException e) {
    System.err.println("Too many requests, back off and retry");
} catch (ApiException e) {
    System.err.println("API error [" + e.getStatusCode() + "]: " + e.getMessage());
}
Classe Generata per Codice di stato
AuthenticationException Chiave API mancante, non valida o non autorizzata 401, 403
QuotaException HTTP 402 402
RateLimitException Troppe richieste 429
ApiException Qualsiasi altra risposta 4xx o 5xx il codice effettivo
EnconvertException Classe base, generata anche in caso di errore di trasporto, richiesta interrotta o file di input illeggibile nessuno

La validazione lato client (una coppia di conversione non supportata, uno schema distill mancante, un aggiornamento watcher vuoto, il numero sbagliato di output per perceiveDirect) genera IllegalArgumentException prima che venga inviata qualsiasi richiesta. La mappa dei messaggi per le risposte del server si trova nel riferimento Codici di errore.


Recupero dei timeout#

I rendering di URL lunghi e le conversioni di documenti di grandi dimensioni possono superare il timeout del reverse proxy anche quando la conversione stessa alla fine riesce. L'SDK gestisce il caso in modo trasparente:

  1. Prima di ogni richiesta con URL singolo o upload di file, l'SDK genera un UUID e lo invia come job_id.
  2. Se la richiesta torna con 5xx, l'SDK passa al polling di GET /v1/convert/status/{jobId} ogni 3 secondi.
  3. Su success restituisce il risultato. Su failed genera ApiException con il messaggio di errore del server.
  4. Il limite di tempo per il polling è di 5 minuti. Superato quello, genera ApiException(504, "Conversion timed out").

Non devi scrivere codice per questo. Se una risposta riuscita omette job_id, l'SDK reinserisce l'id che ha generato, così result.jobId() è sempre utilizzabile con getJobStatus.

Gli invii batch dei siti web sono esclusi di proposito. convertWebsiteToPdf e convertWebsiteToScreenshot non hanno una riga per singolo job da interrogare, quindi un 5xx in quel punto significa che l'invio stesso è fallito e viene riportato direttamente. Nemmeno gli endpoint V2 usano il polling dei job: i loro flussi asincroni passano da getPerceiveBatch e getIngestJob.

Configurazione#

Enconvert client = Enconvert.builder(System.getenv("ENCONVERT_API_KEY"))
        .baseUrl("https://api.enconvert.com")
        .timeout(Duration.ofSeconds(300))
        .build();

Sono disponibili tre costruttori come scorciatoia: new Enconvert(apiKey), new Enconvert(apiKey, baseUrl) e new Enconvert(apiKey, baseUrl, timeout).

Opzione Tipo Predefinito Descrizione
apiKey String obbligatorio Chiave API privata. Un valore null o vuoto genera IllegalArgumentException.
baseUrl String https://api.enconvert.com URL base dell'API. Gli slash finali vengono rimossi.
timeout Duration 300 secondi Applicato sia come timeout di connessione sia come timeout per singola richiesta.

La chiave viaggia nell'header X-API-Key. Gli URL di download presigned vengono recuperati senza di essa, dato che sono già firmati. I tipi di chiave sono trattati in Autenticazione; crea e gestisci le chiavi nella dashboard.

Non inserire mai la chiave API direttamente nel codice. Leggila da una variabile d'ambiente o dal tuo secret manager. L'SDK è solo lato server: una chiave privata non deve finire dentro un artefatto desktop o mobile che un utente può aprire.

Struttura del risultato#

Ogni conversione di un singolo file o di un singolo URL restituisce lo stesso record:

public record ConversionResult(
        String presignedUrl,
        String objectKey,
        String filename,
        Long fileSize,
        Double conversionTimeSeconds,
        String jobId) {}

L'URL presigned ha una durata limitata. Passa saveTo (oppure recupera l'URL tu stesso) e archivia i byte nel tuo bucket se ti servono oltre quella durata.

Gli altri record di risposta che toccherai più spesso:

Record Accessori principali
JobStatus status() (processing, success, failed), presignedUrl(), objectKey(), error()
BatchStatus status(), total(), completed(), failed(), inProgress(), zipDownloadUrl(), items()
PerceiveResult operationId(), renderQuality(), statusCode(), deductions(), outputs(), structured(), cacheHit(), warnings()
V2OutputArtifact url(), objectKey(), sizeBytes(), contentType(), expiresIn()
PerceiveDirectResult content(), contentType(), filename(), renderQuality(), contentHash()
IngestJob jobId(), status(), pagesProcessed(), totalChunks(), outputUrl(), webhookDelivered()
Watcher watcherId(), status(), frequencyMinutes(), checksCount(), nextCheckAt(), lastChangeAt()

I campi la cui forma è definita dalla tua richiesta (structured, data, trackFields, changes degli snapshot) sono esposti come JsonObject di Gson e passano intatti. Le enumerazioni con valori stringa restano String invece di diventare costanti enum Java, così un valore API più recente non rompe mai la deserializzazione su una build più vecchia dell'SDK. com.enconvert.model.v2.V2Enums contiene ogni valore accettato come costante a prova di refuso.


Sorgente e problemi#


Domande frequenti#

Come converto i file in Java con una dipendenza Maven?#

Aggiungi com.enconvert:enconvert-sdk:0.0.1 al tuo pom.xml oppure al build.gradle, costruisci un client con new Enconvert(System.getenv("ENCONVERT_API_KEY")) e chiama un metodo tipizzato come convertUrlToPdf, convertImage, convertDocument oppure convertToPdf. Passa saveTo sul builder delle opzioni per scrivere l'output direttamente su disco invece di gestire tu stesso l'URL presigned.

Come converto un URL in PDF in Java?#

Chiama client.convertUrlToPdf(url, UrlToPdfOptions.builder().saveTo("page.pdf").build()). Imposta singlePage(false) per paginare usando pdfOptions.pageSize invece di produrre una singola pagina continua, e passa auth, cookies oppure headers per una pagina protetta da login.

Come converto DOCX in PDF in Java?#

Chiama client.convertDocument(Path.of("report.docx"), ConvertDocumentOptions.builder().saveTo("report.pdf").build()). Il formato di output predefinito è pdf, quindi imposti outputFormat solo quando vuoi qualcos'altro, per esempio yaml da un input .json. Per i formati senza una coppia dedicata, come EPUB o RTF, usa convertToPdf.

Come converto HEIC in WebP in Java?#

Chiama client.convertImage(Path.of("photo.heic"), ConvertImageOptions.builder("webp").saveTo("photo.webp").build()). Il formato di input viene ricavato dall'estensione del file e il formato di output è l'argomento obbligatorio del builder. jpeg, png, svg, heic e webp si convertono tutti tra loro, e pdf viene rasterizzato in jpeg. Su questo client non esiste un metodo di compressione sul posto.

Come estraggo una pagina web in Markdown pulito da Java?#

Ci sono due strade. client.convertUrlToMarkdown(url, ...) restituisce Markdown in stile GitHub Flavored con frontmatter YAML come file scaricabile. client.v2.perceive(url, PerceiveOptions.builder().outputs(List.of("markdown")).build()) restituisce lo stesso contenuto come artefatto pronto per gli agenti, con un punteggio renderQuality, opzioni di estrazione e controllo della cache. Usa perceive quando una lettura difettosa deve essere rilevabile invece che silenziosa.

Che cos'è renderQuality e perché ogni lettura ne ha uno?#

renderQuality è un punteggio da 0.0 a 1.0 associato a ogni rendering V2. Un punteggio alto significa che la pagina è stata renderizzata in modo pulito; un punteggio basso significa che qualcosa si è messo in mezzo, per esempio una challenge anti-bot, un cookie wall, una schermata di login, una pagina di errore HTTP o uno shell SPA vuoto. Il contenuto viene comunque restituito, con warnings() e deductions() popolati, così la tua pipeline può scartare o ripetere la lettura invece di dare in pasto a un modello una pagina di challenge come se fosse l'articolo.

Come trasformo un sito di documentazione in chunk pronti per il RAG in Java?#

Chiama client.v2.ingest(IngestOptions.builder().mode("sitemap").url("https://docs.example.com").maxPages(100).chunk(new IngestChunkOptions(512, 1)).build()). Il job è asincrono, quindi puoi interrogare getIngestJob(jobId) finché lo stato non è completed e leggere outputUrl() per il JSONL, oppure impostare webhookUrl e verificare la firma HMAC con il secret restituito da getWebhookSecret(). Per documenti locali invece di un sito, usa ingestFiles.

Come gestisce l'SDK le conversioni che superano il timeout del proxy?#

Prima di ogni richiesta con URL singolo o upload di file genera un UUID e lo invia come job_id. Se la richiesta restituisce 5xx, interroga GET /v1/convert/status/{jobId} ogni 3 secondi finché il job non riporta success oppure failed, con un limite di 5 minuti oltre il quale genera ApiException(504, "Conversion timed out"). Gli invii batch di interi siti sono esclusi, perché non hanno una riga per singolo job da interrogare.

Quale versione di Java richiede l'SDK e che cosa porta con sé?#

Java 17 o versioni successive. L'HTTP passa dal java.net.http.HttpClient del JDK, e Gson è l'unico artefatto di terze parti sul classpath. Le risposte sono record Java, quindi uno switch moderno o un pattern match su di esse funziona come previsto. Il client è thread safe: tieni una sola istanza e condividila.