最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于JavaScript實現(xiàn)在線PDF壓縮工具

 更新時間:2026年04月28日 09:01:50   作者:兔子昂  
這篇文章主要為大家詳細(xì)介紹了如何基于JavaScript實現(xiàn)在線PDF壓縮工具,整體思路是優(yōu)先處理?PDF?內(nèi)已經(jīng)存在的?JPEG?圖片資源,再把處理后的內(nèi)容重新保存成新的?PDF?文件,感興趣的小伙伴可以了解下

這篇只講本項目里“PDF壓縮”工具的功能層 JavaScript 實現(xiàn)。整個鏈路可以概括為:

選擇 PDF -> 解析壓縮參數(shù) -> 讀取并加載 PDF -> 找出可重壓縮的 JPEG 圖片流 -> 重新編碼 -> 生成新 PDF -> 下載結(jié)果

這個工具的核心思路很明確:不直接“重寫整個 PDF”,而是優(yōu)先處理 PDF 內(nèi)已經(jīng)存在的 JPEG 圖片資源,再把處理后的內(nèi)容重新保存成新的 PDF 文件。

1. 參數(shù)先歸一化,避免壓縮流程分支失控

工具支持兩種模式:jpeg-only 和 condense-lite。前者真正處理 PDF 內(nèi)圖片,后者主要做輕量整理。壓縮前會先把算法、畫質(zhì)和縮放比例統(tǒng)一成穩(wěn)定值。

function resolveAlgorithm(algorithm) {
  if (algorithm === PDF_COMPRESS_ALGORITHM_CONDENSE_LITE) {
    return PDF_COMPRESS_ALGORITHM_CONDENSE_LITE;
  }
  return PDF_COMPRESS_ALGORITHM_JPEG_ONLY;
}
function resolveQuality(level) {
  return QUALITY_MAP[level] || QUALITY_MAP.medium;
}
function resolveScale(scale) {
  var value = Number(scale);
  if (!Number.isFinite(value) || value <= 0 || value > 1) {
    return 1;
  }
  return value;
}

這樣做的好處是,后面的壓縮主流程只接收規(guī)范參數(shù),不需要到處判斷非法輸入。

2. 先從 PDF 里篩出真正可壓縮的 JPEG 圖片流

PDF 里并不是所有資源都適合壓縮。實現(xiàn)里會遍歷 PDF 的間接對象,只保留同時滿足這幾個條件的對象:

  • 類型是 XObject
  • 子類型是 Image
  • 過濾器里包含 DCTDecode
function listJpegStreams(pdfDoc) {
  var imageStreams = [];
  var entries = pdfDoc.context.enumerateIndirectObjects();

  for (var i = 0; i &lt; entries.length; i += 1) {
    var obj = entries[i][1];
    if (!(obj instanceof PDFRawStream)) continue;

    var dict = obj.dict;
    var type = dict.get(PDFName.of("Type"));
    var subtype = dict.get(PDFName.of("Subtype"));
    var filter = dict.get(PDFName.of("Filter"));
    var filterText = filter ? String(filter.toString()) : "";

    if (String(type || "") !== "/XObject") continue;
    if (String(subtype || "") !== "/Image") continue;
    if (filterText.indexOf("DCTDecode") === -1) continue;

    imageStreams.push({ obj: obj });
  }

  return imageStreams;
}

這里的關(guān)鍵點是只處理 JPEG 流。這樣可以避開很多復(fù)雜格式分支,讓壓縮行為更可控。

3. 圖片重壓縮的核心是“轉(zhuǎn)成位圖再重新編碼”

拿到 JPEG 字節(jié)后,不是直接改二進(jìn)制,而是走瀏覽器圖像鏈路:Blob -> ObjectURL -> Image -> Canvas -> toBlob

function recompressJpegBytes(bytes, scale, quality) {
  return new Promise(function (resolve) {
    var blob = new Blob([bytes], { type: "image/jpeg" });
    var objectUrl = URL.createObjectURL(blob);
    var image = new Image();

    image.onload = function () {
      var width = Math.max(1, Math.floor(image.width * scale));
      var height = Math.max(1, Math.floor(image.height * scale));
      var canvas = document.createElement("canvas");
      var context = canvas.getContext("2d");

      canvas.width = width;
      canvas.height = height;
      context.fillStyle = "#FFFFFF";
      context.fillRect(0, 0, width, height);
      context.drawImage(image, 0, 0, width, height);

      canvas.toBlob(function (nextBlob) {
        URL.revokeObjectURL(objectUrl);
        if (!nextBlob) return resolve(null);
        nextBlob.arrayBuffer().then(function (buffer) {
          resolve({
            buffer: new Uint8Array(buffer),
            width: width,
            height: height,
          });
        });
      }, "image/jpeg", quality);
    };

    image.src = objectUrl;
  });
}

這一步?jīng)Q定了壓縮效果:

  • quality 控制 JPEG 輸出質(zhì)量
  • scale 控制像素尺寸縮放
  • fillRect + drawImage 保證重新繪制后的圖像能穩(wěn)定導(dǎo)出

4. 主流程按“讀文件、掃圖片、替換流、保存結(jié)果”推進(jìn)

真正的入口函數(shù)是 compressPdfFile。它會先讀取文件,再用 pdf-lib 加載 PDF,然后根據(jù)模式?jīng)Q定是否遍歷 JPEG 圖片流。

var originalBytes = await file.arrayBuffer();
var pdfDoc = await PDFDocument.load(originalBytes, {
  updateMetadata: false,
});

var jpegStreams = listJpegStreams(pdfDoc);

for (var i = 0; i &lt; jpegStreams.length; i += 1) {
  var stream = jpegStreams[i].obj;
  var streamBytes = stream.contents;
  var converted = await recompressJpegBytes(streamBytes, scale, quality);

  if (
    converted &amp;&amp;
    converted.buffer &amp;&amp;
    converted.buffer.byteLength &lt; streamBytes.byteLength
  ) {
    stream.contents = converted.buffer;
    if (scale !== 1) {
      stream.dict.set(PDFName.of("Width"), pdfDoc.context.obj(converted.width));
      stream.dict.set(PDFName.of("Height"), pdfDoc.context.obj(converted.height));
    }
  }
}

這里有個很重要的判斷:只有新圖片字節(jié)更小,才會替換原始流。這樣不會因為重復(fù)編碼反而把文件變大。

5. 元數(shù)據(jù)清理是另一條獨立壓縮支線

除了圖片重編碼,這個工具還支持移除元數(shù)據(jù)。實現(xiàn)上會刪除文檔信息字典和目錄上的 Metadata。

function stripPdfMetadata(pdfDoc) {
  var context = pdfDoc.context;
  var catalog = pdfDoc.catalog;
  var infoRef = context.trailerInfo.Info;

  if (infoRef) {
    context.delete(infoRef);
    context.trailerInfo.Info = undefined;
  }

  if (catalog &amp;&amp; catalog.has(PDFName.of("Metadata"))) {
    catalog.delete(PDFName.of("Metadata"));
  }
}

這部分不依賴圖片內(nèi)容,所以即使 PDF 里沒有可處理的 JPEG,仍然可能通過清理元數(shù)據(jù)得到更小的結(jié)果文件。

6. 進(jìn)度、取消和結(jié)果信息都在同一條狀態(tài)鏈上

頁面層不是簡單地調(diào)用一次壓縮函數(shù),而是把壓縮過程拆成可感知的階段:讀取、解析、掃描圖片、重編碼、清理元數(shù)據(jù)、保存完成。

var result = await compressPdfFile(
  this.selectedFile,
  {
    algorithm: this.algorithm,
    quality: this.qualityLevel,
    scale: this.scaleValue,
    removeMetadata: this.removeMetadata,
    cancelToken: this.cancelToken,
  },
  function (progress) {
    self.progressPercent = Math.max(0, Math.min(100, progress.percent || 0));
    self.progressStage = String(progress.stage || "");
    self.progressTotal = Number(progress.total) || 0;
    self.progressProcessed = Number(progress.processed) || 0;
    self.progressReplacedCount = Number(progress.replacedCount) || 0;
  },
);

取消機(jī)制也很直接,本質(zhì)上是一個共享標(biāo)記:

export function createPdfCompressCancelToken() {
  return { cancelled: false };
}

function throwIfCancelled(cancelToken) {
  if (cancelToken &amp;&amp; cancelToken.cancelled) {
    throw createPdfCompressCancelledError();
  }
}

壓縮流程中的關(guān)鍵節(jié)點都會檢查這個標(biāo)記,這樣用戶點取消后,任務(wù)能在下一段異步邊界及時停止。

7. 導(dǎo)出結(jié)果時順手補(bǔ)齊“用戶可理解”的信息

壓縮完成后,工具不會只返回一個 Blob,還會把輸出文件名、壓縮前后體積、替換圖片數(shù)量、壓縮比例一起整理出來。

var result = {
  outputBlob: outputBlob,
  outputName: buildCompressedPdfName(file &amp;&amp; file.name),
  jpegCount: jpegCount,
  replacedCount: replacedCount,
  metadataRemoved: metadataRemoved,
  originalSize: Number(file &amp;&amp; file.size) || 0,
  outputSize: outputBlob.size,
  ratio: getPdfCompressionRatio(file &amp;&amp; file.size, outputBlob.size),
  algorithm: algorithm,
};

這樣頁面層只需要接結(jié)果并創(chuàng)建下載鏈接,就能完整展示這次壓縮是否生效、壓縮了多少、實際處理了多少張圖片。

這套 PDF 壓縮功能的核心并不復(fù)雜,重點是把瀏覽器圖像能力和 pdf-lib 的對象操作串起來:先定位 PDF 內(nèi)可處理的 JPEG 資源,再重編碼、替換、保存,最后把整個過程包裝成普通用戶可直接使用的 Vue 工具。

到此這篇關(guān)于基于JavaScript實現(xiàn)在線PDF壓縮工具的文章就介紹到這了,更多相關(guān)JavaScript壓縮PDF內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

武安市| 富阳市| 普陀区| 连州市| 泾源县| 舒城县| 炉霍县| 进贤县| 咸宁市| 兴宁市| 百色市| 那坡县| 澄江县| 安顺市| 长沙市| 九江县| 呼和浩特市| 灵武市| 沧源| 玉山县| 赤峰市| 新竹市| 乐都县| 文山县| 娄底市| 讷河市| 淳化县| 射洪县| 南安市| 房产| 威信县| 文登市| 合作市| 大英县| 丹东市| 湄潭县| 阿鲁科尔沁旗| 镇原县| 新田县| 汽车| 胶州市|