PPDF123
How-to2026-09-28อ่านประมาณ 5 นาที

SDK TypeScript สำหรับ PDF API: ห้านาทีถึงการเรียกครั้งแรก และสิ่งที่ยังเป็นหน้าที่ของคุณ

ใช้ @pdf123/sdk จาก TypeScript เพื่อรวมไฟล์ ใส่ลายน้ำ อ่านข้อมูลไฟล์ และต่อเครื่องมือหลายตัวเป็นคำขอเดียว SDK จับชื่อเครื่องมือหรือพารามิเตอร์ที่สะกดผิดได้ก่อนอัปโหลดอะไรทั้งสิ้น เมื่อเซิร์ฟเวอร์ปฏิเสธ ผลที่ได้จะมีทั้งเหตุผลและคำแนะนำ แต่การลองใหม่ การยกเลิก และความล้มเหลวบางส่วนในงานเป็นชุดต้องจัดการเอง

PDF123 · Updated 2026-09-28

เมื่อคุณเรียก PDF API จาก Node ชุดพัฒนาซอฟต์แวร์ (SDK) @pdf123/sdk จะจับชื่อเครื่องมือหรือพารามิเตอร์ที่สะกดผิดได้ก่อนอัปโหลดไฟล์ใด ๆ แต่ SDK ไม่ลองใหม่ ไม่ยกเลิกให้ และไม่สตรีมผลลัพธ์ new Pdf123Client() ชี้ไปที่ https://pdf123.xyz แบบไม่ระบุตัวตนโดยค่าเริ่มต้น ไฟล์ของคุณถูกอัปโหลดไปประมวลผลที่นั่น เพราะไม่มี engine ในเครื่อง

แผนภาพ: หนึ่งการเรียกผ่านสามด่านตามลำดับ คือการตรวจ tsc ตอนคอมไพล์ การตรวจสอบตอนรันก่อนส่งคำขอ และ 400 ที่เซิร์ฟเวอร์ส่งกลับหลังอัปโหลด การลองใหม่และการยกเลิกเป็นหน้าที่ของผู้เรียก และอินพุตรวมกันเกิน 95 MiB จะถูกอัปโหลดเป็นชิ้น ๆ โดยอัตโนมัติ

มี PDF สองไฟล์ในโฟลเดอร์ก็รวมได้แล้ว

ต้องใช้ Node 20.3 ขึ้นไป หรือ Bun แพ็กเกจเป็น ES module ให้เก็บโค้ดในไฟล์ .mjs หรือรัน npm pkg set type=module ก่อน วาง a.pdf และ b.pdf ไว้ในไดเรกทอรีปัจจุบัน

npm install @pdf123/sdk
// merge.mjs
import { Pdf123Client } from "@pdf123/sdk";
import { readFileInput, saveResult } from "@pdf123/sdk/node";

const client = new Pdf123Client();
const merged = await client.run("merge", {
  input: [await readFileInput("a.pdf"), await readFileInput("b.pdf")],
});
console.log(await saveResult(merged, { output: "merged.pdf" }));

หลังรัน node merge.mjs เทอร์มินัลจะพิมพ์พาธผลลัพธ์ที่คุณส่งเข้าไป คือ merged.pdf และไฟล์อยู่ในไดเรกทอรีปัจจุบัน readFileInput และ saveResult อยู่ใน @pdf123/sdk/node ส่วน entry หลักพึ่งพาแค่ fetch และรับอินพุตเป็น { name, data } ธรรมดา สภาพแวดล้อมที่ไม่มีระบบไฟล์จึงใช้ไคลเอนต์ตัวเดียวกันได้ ผลลัพธ์คือ { data, contentType, filename, json } โดย data เป็น Uint8Array ทั้งก้อน และ Buffer.from(result.data) จะให้ Buffer

รวม PDF เป็นเพียงหนึ่งในเครื่องมือเท่านั้น

ไฟล์กลับมาใน data รายงานกลับมาใน json

เครื่องมือทุกตัวเรียกด้วย client.run(toolName, { input, params }) โค้ดด้านล่างต่อจาก merge.mjs ในหัวข้อก่อนหน้า โดยมี client และ saveResult อยู่ในสโคปแล้ว จะอ่านผลลัพธ์จากที่ไหนขึ้นกับฟิลด์ returns จาก getTool ซึ่งเป็น "file" หรือ "json" ใช้ data กับแบบแรก และ json กับแบบหลัง

import { getTool } from "@pdf123/sdk";
import { readFileInput, saveResult } from "@pdf123/sdk/node";

// client comes from merge.mjs in the previous section
const marked = await client.run("watermark", {
  input: await readFileInput("a.pdf"),
  params: { watermarkText: "DRAFT", fontSize: 40 },
});
console.log(await saveResult(marked, { output: "marked.pdf" }));

const info = await client.run("get-info", { input: await readFileInput("a.pdf") });
console.log(info.json.FileSize, info.json.Encrypted);
console.log(getTool("get-info")?.returns); // "json"

บล็อกแรกเขียนไฟล์ที่ใส่ลายน้ำลงใน marked.pdf ส่วนบล็อกที่สอง info.json คือรายงาน คุณไม่ต้องจำชื่อฟิลด์ ค่าเริ่มต้น หรือค่าที่อนุญาต เพราะ getTool("watermark")?.fields คือแคตตาล็อกนั้น และ pdfx describe watermark บนบรรทัดคำสั่งก็อ่านชุดเดียวกัน TOOLS เก็บเครื่องมือทั้ง 95 ตัว เอกสารอ้างอิงฉบับเต็มอยู่ในคู่มือ SDK บนหน้านักพัฒนา

ถ้าจะรวม ใส่ลายน้ำ แล้วบีบอัด คุณใช้ pipeline หรือเรียก run สามครั้งต่อกันก็ได้ ขึ้นกับว่าต้องการไฟล์ระหว่างทางหรือไม่ ตรงนี้ก็ต่อจาก client ข้างบนเช่นกัน pipeline พับสามขั้นตอนเป็นคำขอเดียว ผลลัพธ์ระหว่างทางอยู่บนเซิร์ฟเวอร์ และผู้เรียกได้เฉพาะขั้นสุดท้าย ซึ่งโค้ดด้านล่างเขียนลง out.pdf ถ้าต้องการไฟล์จากทุกขั้นตอน ให้เรียกแยกกัน

const result = await client.pipeline(
  [{ tool: "merge" }, { tool: "watermark", params: { watermarkText: "DRAFT" } }, { tool: "compress" }],
  [await readFileInput("a.pdf"), await readFileInput("b.pdf")],
);
console.log(await saveResult(result, { output: "out.pdf" }));

pipeline มีได้อย่างมาก 8 ขั้นตอน ขั้นที่ 9 จะได้ HTTP 400: at most 8 pipeline steps allowed

ไม่ระบุตัวตน ใช้คีย์ หรือชี้ไปที่เซิร์ฟเวอร์ของคุณเอง

ถ้าคุณตั้ง PDFX_API_BASE แล้วคำขอยังไปที่ https://pdf123.xyz นั่นเพราะ new Pdf123Client() ไม่อ่านตัวแปรสภาพแวดล้อม มันดูแค่ตัวเลือกใน constructor

สำหรับการเรียกแบบไม่ระบุตัวตน ใช้ constructor นั้นตามเดิม ถ้าใช้คีย์ ให้เขียน new Pdf123Client({ apiKey: process.env.PDFX_API_KEY }) โดยเฮดเดอร์ของคำขอคือ X-API-KEY

หากต้องการชี้ไปที่เซิร์ฟเวอร์ของคุณเอง ใช้ clientFromEnv() จาก @pdf123/sdk/node ซึ่งอ่าน PDFX_API_BASE และ PDFX_API_KEY หรือจะเขียน new Pdf123Client({ baseUrl: "http://localhost:8080" }) ตรง ๆ ก็ได้ ถ้าเข้าถึงที่อยู่ไม่ได้ การเรียกจะล้มเหลว ไม่มีโหมดออฟไลน์ ส่วนสิ่งที่ได้และต้นทุนเมื่อไฟล์อยู่ในเครือข่ายของคุณเองอธิบายไว้ใน การโฮสต์เองให้อะไรจริง ๆ (และต้องแลกด้วยอะไร)

พารามิเตอร์ผิดจะถูกจับก่อนอัปโหลด

ถ้าไม่มีกลไกนี้ ไฟล์จะถูกอัปโหลดก่อน แล้วชื่อฟิลด์ที่สะกดผิดจึงโผล่เป็น 400 จากเซิร์ฟเวอร์ ชนิดของพารามิเตอร์ของแต่ละเครื่องมือสร้างจากแคตตาล็อก ดังนั้นความผิดพลาดที่พบบ่อยสามแบบจะล้มตั้งแต่ขั้น tsc:

สิ่งที่คุณเขียน ข้อผิดพลาดจากคอมไพเลอร์ (ตัดตอน)
client.run("rotat", { input }) Argument of type '"rotat"' is not assignable to parameter of type 'ToolId'
params: { watermarkTxt: "DRAFT" } Object literal may only specify known properties, but 'watermarkTxt' does not exist, ending with Did you mean to write 'watermarkText'?
params: { angle: 45 } (rotate รับเฉพาะ 90, 180, 270) Type '45' is not assignable to type …, followed by the allowed values

การเรียกที่ถูกต้อง เช่น { angle: 90 } หรือ { watermarkText: "DRAFT", fontSize: 30 } คอมไพล์ผ่าน ฟิลด์ตัวเลขรับได้ทั้งตัวเลขและสตริง ดังนั้น fontSize: 30 กับ fontSize: "30" เทียบเท่ากัน โปรเจกต์ที่ไม่ใช้ TypeScript จะได้ข้อผิดพลาดแบบเดียวกันตอนรัน และคำขอจะไม่ถูกส่งออกไปเลย กรณีที่สามให้ Field "angle" of "rotate" must be one of: 90, 180, 270 ส่วนชื่อเครื่องมือที่สะกดผิดให้ unknown_tool พร้อมรายชื่อที่คล้ายกันในข้อความ

เมื่อเซิร์ฟเวอร์ปฏิเสธ ให้แตกกรณีตาม reason

เมื่อเซิร์ฟเวอร์ปฏิเสธคำขอ SDK จะโยน Pdf123Error ที่มี status, code, reason และ problem.hint code หนึ่งค่ามี reason ได้หลายค่า จึงควรตรวจ reason ก่อนแล้วค่อยถอยมาที่ code ความล้มเหลวที่พบบ่อยสามแบบ รันในเครื่องด้วยอินพุตเดียวกัน หน้าตาเป็นดังนี้:

อินพุต status code reason hint (ต้นฉบับเป็นภาษาอังกฤษ)
PDF ที่เข้ารหัส ไม่ได้ให้รหัสผ่าน 400 bad_request password_required Provide the document password, or unlock the PDF first
รหัสผ่านผิด 400 bad_request wrong_password Check the password and try again
ไม่ใช่ PDF หรือไฟล์เสียหาย 400 invalid_document invalid_pdf Upload a valid, undamaged PDF; you can try repairing it first
import { Pdf123Client, Pdf123Error } from "@pdf123/sdk";
import { readFileInput } from "@pdf123/sdk/node";

const client = new Pdf123Client();
const input = await readFileInput("locked.pdf");
try {
  await client.run("compress", { input });
} catch (error) {
  if (!(error instanceof Pdf123Error)) throw error;
  console.error(error.status, error.code, error.reason, error.problem?.hint);
  if (error.reason === "password_required") {
    await client.run("compress", { input, password: "secret" });
  }
}

เมื่อรู้รหัสผ่าน ให้ส่ง password สำหรับไฟล์เดียว การปลดล็อกและเครื่องมือเป้าหมายจะเกิดในคำขอเดียวกัน ถ้าไฟล์เสียหาย ลองซ่อม PDF ก่อน

"ไม่มีผลลัพธ์" ต้องแยกให้ออก เมื่อ PDF เป็น CSV ไม่พบตารางใน PDF เซิร์ฟเวอร์จะตอบ 204 และ SDK จะโยน code: "no_content" เหตุผลอยู่ใน ส่งออกตารางว่าง (204): PDF ของคุณน่าจะไม่มีคอลัมน์ เครื่องมือกรองตามเงื่อนไขถือว่าเงื่อนไขเป็นเท็จเป็นผลลัพธ์ปกติ จึงไม่โยนข้อผิดพลาด และคืน matched: false พร้อม data ว่าง

การลองใหม่ หน่วยความจำ และการยกเลิกเป็นหน้าที่ของผู้เรียก

ข้อผิดพลาดของเครือข่ายและการตอบ 5xx ถูกโยนออกมาตามจริง SDK ไม่ลองใหม่เองเลย สำหรับคำขอที่เปลี่ยนสถานะ ให้ส่ง idempotencyKey ของคุณเอง การส่งซ้ำด้วยคีย์เดิมจะคืนผลลัพธ์แรกแทนการประมวลผลใหม่ กลไกและขีดจำกัดอยู่ใน Idempotency-Key: การลองใหม่ที่ปลอดภัยสำหรับงาน PDF ผลลัพธ์ถูกอ่านเข้าหน่วยความจำทั้งก้อน ไม่มีการสตรีม เมื่อทั้งอินพุตและเอาต์พุตมีขนาดใหญ่ ให้คำนวณหน่วยความจำที่ทั้งสองใช้พร้อมกัน

การยกเลิกกับการหมดเวลาเป็น code สองค่าที่ต่างกัน และการเรียกหนึ่งครั้งจะรายงานเฉพาะค่าที่เกิดก่อนเท่านั้น ตัวอย่างด้านล่างจึงทดสอบทั้งสองกรณีด้วยการเรียกแยกกันสองครั้ง เพื่อให้ทั้งสองสาขารันได้จริง:

import { Pdf123Client, Pdf123Error } from "@pdf123/sdk";
import { readFileInput } from "@pdf123/sdk/node";

const client = new Pdf123Client();
const input = await readFileInput("a.pdf");

const controller = new AbortController();
setTimeout(() => controller.abort(), 10_000);
try {
  await client.run("compress", { input, signal: controller.signal });
} catch (error) {
  if (error instanceof Pdf123Error && error.code === "cancelled") { /* คุณยกเลิกเอง */ }
}

try {
  await client.run("compress", { input, timeoutMs: 60_000 });
} catch (error) {
  if (error instanceof Pdf123Error && error.code === "timeout") { /* เกิน timeoutMs รอบนี้ไม่มี signal */ }
}

ทันทีที่สัญญาณยกเลิก (AbortSignal) ทำงาน คำขอจะถูกปฏิเสธด้วย code: "cancelled" ถ้าเกินเวลาจะได้ code: "timeout" แต่ละคำขอมีเวลาหมดโดยค่าเริ่มต้น 5 นาที เปลี่ยนได้ตอนสร้างไคลเอนต์ หรือแทนที่ทีละการเรียกด้วย timeoutMs ในการอัปโหลดเป็นชิ้น ทุกคำขอถือเวลาหมดนี้ของตัวเอง การยกเลิกเพียงปิดการเชื่อมต่อนี้ ไม่รับประกันว่าเซิร์ฟเวอร์จะหยุดประมวลผล

ไฟล์หนึ่งล้มเหลว ที่เหลือทำต่อ และ callback มาตามลำดับที่เสร็จ

สำหรับอินพุตเป็นชุดกับเครื่องมือที่รับไฟล์เดียว ใช้ runBatch ถ้าไฟล์หนึ่งล้มเหลว ไฟล์อื่นยังทำต่อ onResult ถูกเรียกทันทีที่แต่ละไฟล์เสร็จ จึงทำงานตามลำดับที่เสร็จ และ index คือตำแหน่งของไฟล์ในอาร์เรย์อินพุต ส่วนอาร์เรย์ที่คืนมาเรียงตามอินพุตเสมอ ให้เขียนไบต์ลงดิสก์ภายใน callback นี้ เพราะ retainData: false จะล้าง data ทันทีที่ callback คืนค่า ผลลัพธ์ที่ไม่ได้ saveResult ตรงนี้จึงหายไป หากการรันถูกขัดจังหวะ ไฟล์ที่เขียนไปแล้วยังอยู่

สมมติว่าในโฟลเดอร์มี PDF ที่ใช้ได้สามไฟล์ คือ a.pdf, b.pdf และ locked.pdf ที่เข้ารหัส (รหัสผ่าน secret) กับไฟล์เสีย broken.pdf ที่มีแค่ตัวอักษรไม่กี่ตัว:

import { Pdf123Client } from "@pdf123/sdk";
import { fileSource, saveResult } from "@pdf123/sdk/node";

const client = new Pdf123Client();
const results = await client.runBatch("compress", [
  fileSource("a.pdf"), fileSource("broken.pdf"), fileSource("b.pdf"), fileSource("locked.pdf"),
], {
  concurrency: 2,
  passwordFor: (file) => (file.name === "locked.pdf" ? "secret" : undefined),
  onResult: async (entry, index) => {
    console.log(index, entry.input.name, entry.ok ? "ok" : entry.error.reason);
    if (entry.ok) await saveResult(entry.result, { output: "out/" });
  },
  retainData: false,
});

โดยค่าเริ่มต้นจะประมวลผลครั้งละสองไฟล์ ปรับได้ด้วย concurrency fileSource(path) อ่านไฟล์จากดิสก์เมื่อถึงคิวเท่านั้น งานชุดใหญ่ของไฟล์ใหญ่จึงไม่ถูกโหลดเข้าหน่วยความจำพร้อมกันทั้งหมด passwordFor ทำให้งานชุดที่ผสมไฟล์ล็อกและไม่ล็อกรันจบในรอบเดียว เมื่อรันสี่ไฟล์ข้างบนในเครื่อง callback ได้รับ:

1 broken.pdf invalid_pdf
0 a.pdf ok
2 b.pdf ok
3 locked.pdf ok

ลำดับที่เสร็จอาจต่างกันทุกครั้ง นี่เป็นเพียงหน้าตาของการรันหนึ่งครั้ง retainData: false ทำให้ data ในอาร์เรย์ที่คืนมาว่าง ไฟล์ที่สำเร็จถูกเขียนลง out/ โดย saveResult ข้างบนไปแล้ว เมื่อส่ง idempotencyKey ให้ runBatch คีย์ที่ส่งจริงของแต่ละไฟล์คือ <key>:<index>

อัปโหลดเป็นชิ้นเริ่มเมื่อรวมกันเกิน 95 MiB เท่านั้น

เนื้อหาคำขอแบบตรงมีขีดจำกัด 100 MiB และที่ใหญ่กว่านั้นจะถูกปฏิเสธ ดูรายละเอียดใน เมื่ออัปโหลด PDF ขนาดใหญ่แล้วถูกปฏิเสธ: ขีดจำกัด 100 MiB ของบอดีคำขอ และข้อผิดพลาดที่ชี้ผิดทาง เมื่อไฟล์ทั้งหมดในคำขอเดียวรวมกันเกิน 95 MiB SDK จะสลับไปอัปโหลดเป็นชิ้น และการเรียก client.run ของคุณไม่เปลี่ยน ค่าสูงสุดเริ่มต้นของเซิร์ฟเวอร์สำหรับการอัปโหลดครั้งเดียวคือ 500 MiB เมื่อโฮสต์เอง ปรับด้วย PDFX_UPLOAD_MAX_BYTES

สำหรับการดำเนินการเดียวกันที่เขียนด้วย curl, MCP และบรรทัดคำสั่ง ดู การดำเนินการเดียวกัน สี่ไคลเอนต์: เบราว์เซอร์, curl, MCP, pdfx บทความนั้นวางสี่การเรียกเทียบกัน ส่วนบทความนี้ขยายความเฉพาะ SDK หน้าของแพ็กเกจบน npm คือ @pdf123/sdk

Open tool
Process in the browser — no watermark, files removed after the job.
Open tool
Read next
ใส่เลขหน้าให้ PDF: ตำแหน่ง เลขเริ่มต้น และกับดักหน้าที่ถูกหมุน
วิธีใส่เลขหน้าให้ PDF ที่มีอยู่แล้วทางออนไลน์: เลือกมุม ตั้งเลขเริ่มต้น เช่น 101 และหลีกเลี่ยงเลขที่ตะแคงบนหน้าที่เก็บไว้เป็นหน้าหมุน
แปลง PDF เป็น JPG หรือ PNG: DPI เปลี่ยนอะไร และส่งออกหน้าเดียวอย่างไร
PDF เป็นภาพ เรนเดอร์ทุกหน้าลงไฟล์ ZIP เป็น PNG เว้นแต่คุณเลือก JPEG ฟอร์มบนเบราว์เซอร์มีเพียงรูปแบบและ DPI ฟิลด์ API สำหรับเลือกหน้าและ WebP ไม่มีผล ขนาดไฟล์ที่วัดได้ที่ 150 และ 300 DPI และวิธีส่งออกเพียงหน้าเดียว
รูปถ่ายจากมือถือเป็น PDF: ทำไมไฟล์ใหญ่โตและหน้าหนึ่งหมุนตะแคง
ภาพเป็น PDF วางรูปแต่ละรูปไว้หนึ่งหน้าที่ขนาดพิกเซลเต็ม JPEG ขนาดแบบมือถือ 3.2 MB สามรูปจึงกลายเป็น PDF 41.5 MB และรูปแนวตั้งออกมาอยู่บนหน้าแนวนอน ให้ย่อรูปก่อนแปลง แล้วหมุนหน้าที่ตะแคงภายหลัง