跳转到内容

Node.js

Terminal window
npm install axios
import axios from 'axios';
import fs from 'node:fs';
import path from 'node:path';
import { randomUUID } from 'node:crypto';
const API_KEY = 'sk_your_key';
const BASE = 'https://omnidoc.top/api/v1';
const API_HEADERS = { 'X-API-Key': API_KEY };
async function uploadDocument(filePath: string) {
const stat = fs.statSync(filePath);
const { data: ticket } = await axios.post(
`${BASE}/upload/presign`,
{
filename: path.basename(filePath),
file_size: stat.size,
},
{ headers: API_HEADERS, timeout: 20_000 }
);
await axios.put(ticket.upload_url, fs.createReadStream(filePath), {
headers: {
...ticket.upload_headers,
'Content-Length': stat.size,
},
maxBodyLength: Infinity,
timeout: 300_000,
});
const { data: finalized } = await axios.post(
`${BASE}/upload/finalize`,
{ finalize_token: ticket.finalize_token },
{ headers: API_HEADERS, timeout: 30_000 }
);
return finalized.document;
}
async function createJob(documentId: string, targetLang: string, engine: string) {
const { data } = await axios.post(
`${BASE}/jobs`,
{
document_id: documentId,
target_lang: targetLang,
engine,
},
{
headers: { ...API_HEADERS, 'Idempotency-Key': randomUUID() },
timeout: 30_000,
}
);
return data;
}
async function waitForJob(jobId: string) {
while (true) {
const { data } = await axios.get(`${BASE}/jobs/${jobId}`, {
headers: API_HEADERS,
timeout: 20_000,
});
if (data.status === 'completed') return data;
if (data.status === 'failed') {
throw new Error(data.error_msg || 'translation failed');
}
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}
function responseFilename(contentDisposition: string | undefined, fallback: string) {
const utf8 = contentDisposition?.match(/filename\*=UTF-8''([^;]+)/i);
if (utf8) return path.basename(decodeURIComponent(utf8[1]));
const asciiName = contentDisposition?.match(/filename="([^"]+)"/i);
return asciiName ? path.basename(asciiName[1]) : fallback;
}
async function downloadArtifacts(jobId: string, outputDir: string) {
fs.mkdirSync(outputDir, { recursive: true });
const { data: artifacts } = await axios.get(`${BASE}/artifacts`, {
headers: API_HEADERS,
params: { job_id: jobId },
timeout: 20_000,
});
const outputs: string[] = [];
for (const artifact of artifacts) {
const response = await axios.get(
`${BASE}/artifacts/${artifact.id}/download`,
{
headers: API_HEADERS,
responseType: 'arraybuffer',
timeout: 300_000,
}
);
const filename = responseFilename(
response.headers['content-disposition'],
`${artifact.id}.${artifact.format}`
);
const destination = path.join(outputDir, filename);
fs.writeFileSync(destination, response.data);
outputs.push(destination);
}
return outputs;
}
async function translateDocument(filePath: string, targetLang = 'en') {
const document = await uploadDocument(filePath);
const job = await createJob(document.id, targetLang, 'xr-397b');
await waitForJob(job.id);
return downloadArtifacts(job.id, 'omnidoc-output');
}
translateDocument('report.docx').then(console.log).catch(console.error);

浏览器会根据 File 自动设置 Content-Length;Node.js 上传文件流时应显式设置准确值。 两端都必须逐项发送 presign 返回的 upload_headers,且不要把 X-API-Key 发到上传 URL。