20230503 Javascript下载页面pdf的高质量代码

你是否曾经为自己写的代码而感到懊恼?你是否想过如何才能写出高质量代码?那就不要错过这个话题!在这里,我们可以讨论什么是高质量代码,如何写出高质量代码等问题。无论你是初学者还是资深开发人员,都可以在这个话题下进行分享,汲取灵感和知识,共同提高自己的编程水平和工作效率。

一、 前言

低质量代码最可怕,
易生错,难维护。
BUG频发,影响大,
效率低,不规范,
编程难,思路乱,
高质量,从头做,
一点都不难。

二、高质量代码的特征

以下是一些高质量代码的特征:

  1. 可读性强:高质量代码应该易于阅读和理解,采用清晰的命名、注释和缩进,以及结构化的代码布局,可以使代码更加易于理解。

  2. 可维护性强:高质量代码应该易于修改和扩展,采用模块化的设计和代码重构,可以使代码更加易于维护。

  3. 可测试性强:高质量代码应该易于测试和调试,采用单元测试和集成测试等测试技术,可以使代码更加易于测试和调试。

  4. 安全性高:高质量代码应该具有良好的安全性,采用安全编码技术和安全测试技术,可以使代码更加安全可靠。

  5. 性能优良:高质量代码应该具有较好的性能,采用优化算法和数据结构,可以使代码更加高效。

  6. 代码规范:高质量代码应该符合代码规范,采用一致的代码风格和规范的编码习惯,可以使代码更加易于理解和维护。

  7. 可重用性高:高质量代码应该具有较好的可重用性,采用模块化设计和面向对象编程等技术,可以使代码更加易于重用。

三、编程实践

这是一个使用Tampermonkey(或Greasemonkey)脚本在网页中自动下载PDF文件的代码。具体来说,此脚本会监听 https://copyright.lib.*..**/pdfindex.jsp 页面。

代码中的注释已经很详细地解释了脚本中各个部分的功能和作用,这里简要介绍一下:

  • 脚本首先等待页面加载完毕,以便获得结题报告窗口的DOM元素。
  • 脚本在结题报告窗口下面插入一个按钮,用于启动PDF下载。
  • 当用户点击下载按钮时,脚本会自动遍历所有PDF页面并将其转换为一个单独的PDF文件。
  • PDF的每一页都是以图片的形式保存的,脚本会首先获取图片链接,然后使用jQuery的$.get()方法获取图片数据。这个数据在服务器端被压缩成了base64编码,并通过JSON格式返回给客户端。
  • 然后,脚本会将图片数据转换为Blob格式,并通过使用新的Image对象将其加载到页面中。这一步骤将确保jsPDF.addImage()方法只被调用一次,因为该方法需要从图片URL中获取图片数据。
  • 然后,脚本将Image对象添加到jsPDF中,以便在所有页面都添加完成后将它们保存为一个PDF文件。

总体来说,这个脚本使用了多种技术和工具,包括了异步函数、Promise、MutationObserver、jQuery和jsPDF。这使得脚本在处理多个页面和图片下载时非常有效和可靠。

四、 实际代码

// ==UserScript==
// @name         pdf_downloader
// @namespace    https://blog.rhilip.info/
// @version      1.0
// @description  下载pdf
// @author       DR-ZF-
// @match        https://copyright.lib.****.***.**/pdfindex.jsp*
// @require      https://unpkg.com/[email protected]/dist/jspdf.umd.min.js
// @require      https://unpkg.com/[email protected]/dist/jquery.js
// ==/UserScript==

/* globals $, jspdf */

// 参考油猴NSFC_conclusion_downloader by Rhilip,进行改写
// Copy and edit from https://stackoverflow.com/a/61511955
function waitForElm(selector) {
    
    
    return new Promise(resolve => {
    
    
        if ($(selector).length > 0) {
    
    
            return resolve($(selector));
        }

        const observer = new MutationObserver(mutations => {
    
    
            if ($(selector).length > 0) {
    
    
                resolve($(selector));
                observer.disconnect();
            }
        });

        observer.observe(document.body, {
    
    
            childList: true,
            subtree: true
        });
    });
}

(async function() {
    
    
    'use strict';

    // 等待页面加载
    const conclusionReportTab = await waitForElm('#tool_Zoom');



    // 准备交互按钮,并插入到页面中
    const downloadBtn = $('<button type="button" class="el-button el-button--default el-button--medium is-round">下载全文</button>');
    conclusionReportTab.after(downloadBtn);



    // 点击交互按钮时需要开始下载操作
    downloadBtn.click(async () => {
    
    
        downloadBtn.prop('disabled', true).addClass('is-disabled');
        if (!/无页面/.test(conclusionReportTab.text())) {
    
    
            // 获得项目信息
            const urlParams = new URLSearchParams(location.search);
            const dependUintID = urlParams.get('fid');

            


            // 准备需要的PDF文件,并删除初始页
            const doc = new jspdf.jsPDF();
            doc.deletePage(1);
            doc.setDocumentProperties({
    
    
                title: 'pdf_downloader',
                subject: location.href,
                creator: 'pdf_downloader'
            });

            // 核心下载方法
            const image = new Image();
            for (let i=1;;i++) {
    
    
                downloadBtn.text(`正在下载第 ${
      
      i}`);

                // 获得图片链接
                const response = await $.get('/jumpServlet?', {
    
     page: i, fid: dependUintID });

                console.log(response);
                if (!response){
    
    
                    break; // 此处应该明确为false才break
                }

                const jsonResponse = JSON.parse(response);
                const {
    
     list } = jsonResponse;

                let firstSrc, firstId
                if (Array.isArray(list) && list.length > 0) {
    
    
                    const [firstItem] = list;
                    if (firstItem.src && firstItem.id) {
    
    
                        firstSrc = firstItem.src;
                        firstId = firstItem.id;
                    } else {
    
    
                        console.error(`Unexpected response data. Missing 'src' or 'id' property. Response: ${
      
      JSON.stringify(response)}`);
                    }
                } else {
    
    
                    console.error(`Unexpected response data. 'list' property not found or empty array. Response: ${
      
      JSON.stringify(response)}`);
                }



                // 获得Blob形式的imageData,这样可以防止image.src和jsPDF.addImage会产生两次图片请求,浪费带宽
                // (实际变成了一次请求服务器和两次请求本地blob)
                try {
    
    
                    const imageDataAsBlob = await $.ajax({
    
    
                        url: firstSrc,
                        method: 'GET',
                        xhrFields: {
    
     responseType: 'blob'}
                    });
                    // 加载图片并获得图片的 width, height 属性

                    image.src = URL.createObjectURL(imageDataAsBlob);
                    await image.decode();

                    // 将图片添加进PDF中
                    doc.addPage([image.width, image.height], image.width < image.height ? 'p' : 'l');
                    doc.addImage(image, "PNG", 0, 0, image.width, image.height);
                } catch (e) {
    
    
                    break; // 如果中间有任何失败,则直接break
                }
            }

            // 我们并没法 await 保存过程,所以直接显示下载完成就好,浏览器处理好会自动显示下载文件
            doc.save(`PDF下载.pdf`);
            downloadBtn.text('下载完成');
        }
    });

})();

五、 总结

这段代码的主要功能是从网页上下载 PDF 文件。以下是其中的优秀点:

  1. 使用了异步函数和 Promise:代码使用了 async 函数和 Promise 对象,使得在等待页面加载和图片下载等耗时操作时,代码可以继续执行其他任务,从而避免了页面阻塞或卡顿的情况。

  2. 使用了 MutationObserver:使用 MutationObserver 监听 DOM 树变化,当目标元素出现时再执行后续代码,可以提高页面加载速度和性能,同时避免了因页面加载慢而导致的代码执行错误。

  3. 代码可读性高:变量和函数名都具有语义化,注释清晰明了,代码结构清晰简洁,易于理解和维护。

  4. 使用了 jQuery 和 jspdf 库:代码使用了 jQuery 库简化了 DOM 操作,同时使用了 jspdf 库方便地生成 PDF 文件。

  5. 错误处理得当:代码对 HTTP 请求和其他异常情况进行了正确的处理,同时在出现异常时及时终止程序并给出相应的错误提示,防止代码出现严重的运行错误。

猜你喜欢

转载自blog.csdn.net/weixin_44382195/article/details/130469253