OA系统公文一键下载解决方案

一、工作背景

OA系统已经部署在互联网访问环境中,工作人员通过浏览器即可完成公文收发、审批和查阅等工作。但却以“信息安全”、“保密管理”等理由,对公文正文设置各种使用限制,不提供下载、不允许打印,只能在特定的在线阅读页面中查看。安全管理本身当然是必要的,但安全管理的重点应该是明确哪些文件可以下载、谁可以下载、下载后如何管理。

如果一个系统已经允许用户在互联网环境下登录,并且经过身份认证后可以正常查看某份公文,那么真正需要解决的安全问题,应当更多地依靠身份认证、权限控制、访问控制、日志审计、数据加密以及终端安全管理等手段来保障,而不是一味限制正常的办公操作。

而且,更有意思的是,部分OA系统虽然在页面上没有提供下载按钮,但公文正文实际上已经被系统加载到浏览器的在线阅读组件中,并以OFD或PDF等标准电子文档格式提供给阅读器使用。因此,与其让工作人员反复截图、打印后再扫描,或者为了离线查看而采用各种低效的方式,不如在具备正常访问和阅读权限的前提下,提供一种更加规范、方便的本地保存方式。

本文提供了一种方式为OA公文增加本地保存能力,并根据收文单中的公文标题自动命名下载文件,从而实现公文的离线阅读和打印,前提仍然是使用者本身已经具有相应的文件使用权限。

二、适用范围

本文方法主要适用于使用FlexOffice / WebOffice公文在线阅读方式的 OA 系统。

适用于以下场景:

  • 使用Edge浏览器访问 OA 系统;
  • OA系统中的公文正文通过网页在线阅读;
  • 公文正文实际以OFD或PDF格式提供;
  • 系统页面中存在公文正文的在线阅读功能,但没有提供方便的本地下载入口;
  • 需要将公文正文下载到本地,用于离线阅读、保存或打印。

其他浏览器环境和OA系统,只要是FlexOffice / WebOffice模式,那么基础环境设置方式一般都类同,同时简单改动代码中监控地址部分即可实现。

三、部署流程

(一)安装公文阅读软件

由于OA系统中的部分公文正文实际采用OFD或PDF格式,下载后的文件并不是普通的Word文档,因此需要安装支持 OFD 格式的阅读软件。建议安装“数科OFD”或“金山WPS”;PDF支持软件自行选择。

安装完成后,可以直接使用该软件打开下载后的.ofd文件,实现:

  • 离线阅读
  • 打印
  • 页面查看
  • 内容留存

(二)安装浏览器扩展环境

两种方式任选其一(离线环境是给不能使用互联网的设备提供的方法)。

1.在线安装方式:通过浏览器应用商店安装篡改猴。

2.离线安装方式:

本方法使用 Edge 浏览器进行操作。

  • 首先,点击此处下载插件并保存(插件适用Edge和Chrome浏览器);
  • 然后,在目标设备的Edge 浏览器地址栏输入edge://extensions/,按回车进入扩展管理页面,并在页面中打开开发人员模式选项;
  • 最后,将准备好的tampermonkey.crx文件直接拖拽到 Edge 页面内进行添加扩展。

完成后,浏览器中就安装好了篡改猴(Tampermonkey)。

(三)配置篡改猴权限

在浏览器右上角的扩展菜单中找到“篡改猴”。

建议先将篡改猴固定到浏览器工具栏,方便后续使用。

右键点击“篡改猴” → “管理扩展”

进入篡改猴的扩展管理页面。在页面中找到相关权限设置,并打开:

允许用户脚本和允许访问文件URL

以此授权插件获取URL地址来判断是否运行脚本。

(四)安装“公文一键下载”脚本

点击浏览器右上角的:篡改猴

然后选择:添加新脚本

在进入脚本编辑页面后,将编辑器中原有的内容全部删除,然后将下面“公文一键下载”脚本文本完整复制并粘贴到编辑器中。

粘贴完成后按Ctrl + S保存脚本,然后正常访问OA系统即可出现下载按钮实现一键下载公文。如图:

“公文一键下载”脚本(9月8日更新,新增多种办公文件格式支持):

// ==UserScript==
// @name         公文一键下载YY
// @namespace    http://tampermonkey.net/
// @version      6.3.0
// @description  自动获取收文单标题,并在 WebOffice 阅读器中一键下载 OFD/PDF/DOC/DOCX 等常见公文格式
// @match        http://192.171.40.3:8600/*
// @match        http://192.171.40.3:8095/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      192.171.40.3
// @run-at       document-start
// ==/UserScript==

(function () {

    'use strict';

    console.log('[公文下载] 脚本启动');
    console.log('[公文下载] 当前地址:', location.href);


    // =====================================================
    // 配置
    // =====================================================

    const TITLE_KEY = 'current_gongwen_title';


    // =====================================================
    // 清理文件名
    // =====================================================

    function cleanFileName(title) {

        if (!title) {
            return '';
        }

        title = title
            .replace(/^收文[::]\s*/i, '')
            .replace(/[\\/:*?"<>|]/g, '_')
            .replace(/\s+/g, ' ')
            .trim();

        // 删除已经存在的扩展名
        title = title.replace(
            /\.(ofd|doc|docx|pdf|xls|xlsx|ppt|pptx|rtf)$/i,
            ''
        );

        // Windows 文件名不能以空格或点结尾
        title = title.replace(/[. ]+$/g, '');

        return title;
    }


    // =====================================================
    // 从 8600 收文单寻找标题
    // =====================================================

    function findReceiveTitle() {

        // -------------------------------------------------
        // 第一优先级:
        // #goa_doc_receivefile_title
        // -------------------------------------------------

        const textarea =
            document.querySelector(
                '#goa_doc_receivefile_title'
            );

        if (textarea) {

            let title =
                textarea.value ||
                textarea.textContent ||
                '';

            title = cleanFileName(title);

            if (title) {
                return title;
            }
        }


        // -------------------------------------------------
        // 第二优先级:
        // data-uniquefieldname
        // -------------------------------------------------

        const display =
            document.querySelector(
                '[data-uniquefieldname="goa_doc_receivefile_title"]'
            );

        if (display) {

            const pre =
                display.querySelector('pre');

            let title =
                pre
                    ? pre.textContent
                    : display.textContent;

            title = cleanFileName(title);

            if (title) {
                return title;
            }
        }

        return '';
    }


    // =====================================================
    // 保存标题
    // =====================================================

    function saveTitle(title) {

        if (!title) {
            return;
        }

        title = cleanFileName(title);

        if (!title) {
            return;
        }

        GM_setValue(
            TITLE_KEY,
            title
        );

        console.log(
            '[公文下载] 已保存公文标题:',
            title
        );
    }


    // =====================================================
    // 读取标题
    // =====================================================

    async function getSavedTitle() {

        try {

            const title =
                await GM_getValue(
                    TITLE_KEY,
                    ''
                );

            if (title) {
                return cleanFileName(title);
            }

        } catch (e) {

            console.error(
                '[公文下载] 读取标题失败:',
                e
            );
        }

        return '';
    }


    // =====================================================
    // 8600 页面
    // 只负责:
    // 找标题 → 保存标题
    // =====================================================

    function init8600() {

        console.log(
            '[公文下载] 进入 8600 收文单页面'
        );

        let lastTitle = '';


        function checkTitle() {

            const title =
                findReceiveTitle();

            if (
                title &&
                title !== lastTitle
            ) {

                lastTitle = title;

                saveTitle(title);

                console.log(
                    '[公文下载] 当前公文标题:',
                    title
                );
            }
        }


        // 立即检查
        checkTitle();


        // -------------------------------------------------
        // 页面是动态加载的
        // -------------------------------------------------

        const observer =
            new MutationObserver(
                function () {

                    checkTitle();

                }
            );

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


        // -------------------------------------------------
        // 定时检查
        // -------------------------------------------------

        setInterval(
            checkTitle,
            1000
        );
    }


    // =====================================================
    // 获取 8095 的真正文件地址
    // =====================================================

    function getFileUrl() {

        try {

            const url =
                new URL(
                    location.href
                );

            const file =
                url.searchParams.get(
                    'file'
                );

            if (!file) {
                return null;
            }

            console.log(
                '[公文下载] 找到 file:',
                file
            );

            return file;

        } catch (e) {

            console.error(
                '[公文下载] 获取 file 失败:',
                e
            );
        }

        return null;
    }


    // =====================================================
    // 根据文件头和文件内容判断文件类型
    // =====================================================

    function detectFileType(bytes) {

        // -------------------------------------------------
        // PDF
        //
        // 文件头:
        // 25 50 44 46
        // %PDF
        // -------------------------------------------------

        if (
            bytes.length >= 4 &&
            bytes[0] === 0x25 &&
            bytes[1] === 0x50 &&
            bytes[2] === 0x44 &&
            bytes[3] === 0x46
        ) {

            return 'pdf';
        }


        // -------------------------------------------------
        // 传统 Office
        //
        // DOC / XLS / PPT
        //
        // 文件头:
        // D0 CF 11 E0 A1 B1 1A E1
        // -------------------------------------------------

        if (
            bytes.length >= 8 &&
            bytes[0] === 0xD0 &&
            bytes[1] === 0xCF &&
            bytes[2] === 0x11 &&
            bytes[3] === 0xE0 &&
            bytes[4] === 0xA1 &&
            bytes[5] === 0xB1 &&
            bytes[6] === 0x1A &&
            bytes[7] === 0xE1
        ) {

            const sample =
                new TextDecoder('latin1').decode(
                    bytes.slice(
                        0,
                        Math.min(
                            bytes.length,
                            200000
                        )
                    )
                );


            // Word
            if (
                sample.includes('WordDocument') ||
                sample.includes('Microsoft Word')
            ) {

                return 'doc';
            }


            // Excel
            if (
                sample.includes('Workbook') ||
                sample.includes('Microsoft Excel')
            ) {

                return 'xls';
            }


            // PowerPoint
            if (
                sample.includes('PowerPoint Document') ||
                sample.includes('Microsoft PowerPoint')
            ) {

                return 'ppt';
            }


            // 公文场景下如果无法进一步判断,
            // 默认按 DOC 处理
            return 'doc';
        }


        // -------------------------------------------------
        // ZIP
        //
        // OFD / DOCX / XLSX / PPTX
        // 都可能以 PK 开头
        //
        // 所以不能再简单地:
        //
        // PK = OFD
        //
        // -------------------------------------------------

        if (
            bytes.length >= 2 &&
            bytes[0] === 0x50 &&
            bytes[1] === 0x4B
        ) {

            const sample =
                new TextDecoder('latin1').decode(
                    bytes.slice(
                        0,
                        Math.min(
                            bytes.length,
                            2000000
                        )
                    )
                );


            // -------------------------------------------------
            // OFD
            // -------------------------------------------------

            if (
                sample.includes('OFD.xml') ||
                sample.includes('ofd.xml')
            ) {

                return 'ofd';
            }


            // -------------------------------------------------
            // DOCX
            // -------------------------------------------------

            if (
                sample.includes('word/document.xml') ||
                sample.includes('word/styles.xml') ||
                sample.includes(
                    'word/_rels/document.xml.rels'
                )
            ) {

                return 'docx';
            }


            // -------------------------------------------------
            // XLSX
            // -------------------------------------------------

            if (
                sample.includes('xl/workbook.xml') ||
                sample.includes('xl/worksheets/')
            ) {

                return 'xlsx';
            }


            // -------------------------------------------------
            // PPTX
            // -------------------------------------------------

            if (
                sample.includes(
                    'ppt/presentation.xml'
                ) ||
                sample.includes('ppt/slides/')
            ) {

                return 'pptx';
            }


            // 如果是 ZIP,但是无法判断,
            // 为兼容以前版本,仍然按 OFD 处理
            return 'ofd';
        }


        // -------------------------------------------------
        // RTF
        // -------------------------------------------------

        if (bytes.length >= 5) {

            const text =
                new TextDecoder('latin1').decode(
                    bytes.slice(
                        0,
                        20
                    )
                );

            if (
                text.startsWith('{\\rtf')
            ) {

                return 'rtf';
            }
        }


        return 'unknown';
    }


    // =====================================================
    // 创建下载按钮
    // =====================================================

    function createDownloadButton(fileUrl) {

        // 防止重复
        if (
            document.getElementById(
                'ofd-download-button'
            )
        ) {

            return;
        }


        const button =
            document.createElement(
                'button'
            );

        button.id =
            'ofd-download-button';

        button.textContent =
            '⬇ 下载公文';


        button.style.cssText = `
            position: fixed;
            top: 40px;
            right: 20px;
            z-index: 2147483647;

            padding: 10px 18px;

            background: #1677ff;
            color: white;

            border: none;
            border-radius: 6px;

            font-size: 14px;
            font-weight: bold;

            cursor: pointer;

            box-shadow: 0 3px 12px rgba(0,0,0,.25);
        `;


        // =================================================
        // 点击下载
        // =================================================

        button.addEventListener(
            'click',
            async function () {

                console.log(
                    '[公文下载] 开始下载'
                );


                button.textContent =
                    '正在下载...';

                button.style.background =
                    '#fa8c16';


                // -----------------------------------------
                // 获取公文标题
                // -----------------------------------------

                let title =
                    await getSavedTitle();


                if (!title) {

                    title =
                        '公文正文';

                    console.warn(
                        '[公文下载] 没有找到保存的标题'
                    );
                }


                console.log(
                    '[公文下载] 公文标题:',
                    title
                );


                // -----------------------------------------
                // 请求文件
                // -----------------------------------------

                GM_xmlhttpRequest({

                    method: 'GET',

                    url: fileUrl,

                    responseType: 'blob',


                    // -------------------------------------
                    // 成功
                    // -------------------------------------

                    onload:
                        function (response) {

                            console.log(
                                '[公文下载] HTTP状态:',
                                response.status
                            );


                            if (
                                response.status < 200 ||
                                response.status >= 300
                            ) {

                                alert(
                                    '下载失败\nHTTP状态:' +
                                    response.status
                                );


                                button.textContent =
                                    '⬇ 下载公文';

                                button.style.background =
                                    '#1677ff';

                                return;
                            }


                            const blob =
                                response.response;


                            // ---------------------------------
                            // 读取文件头
                            // ---------------------------------

                            const reader =
                                new FileReader();


                            reader.onload =
                                function () {

                                    const bytes =
                                        new Uint8Array(
                                            reader.result
                                        );


                                    // -------------------------
                                    // 输出调试信息
                                    // -------------------------

                                    const headerBytes =
                                        Array.from(
                                            bytes.slice(
                                                0,
                                                16
                                            )
                                        );


                                    console.log(
                                        '[公文下载] 文件前16字节:',
                                        headerBytes
                                    );


                                    console.log(
                                        '[公文下载] 文件头HEX:',
                                        headerBytes
                                            .map(
                                                b =>
                                                    b
                                                        .toString(16)
                                                        .padStart(
                                                            2,
                                                            '0'
                                                        )
                                            )
                                            .join(' ')
                                    );


                                    console.log(
                                        '[公文下载] 文件大小:',
                                        blob.size,
                                        'bytes'
                                    );


                                    // -------------------------
                                    // 自动识别文件类型
                                    // -------------------------

                                    const fileType =
                                        detectFileType(
                                            bytes
                                        );


                                    console.log(
                                        '[公文下载] 检测到文件类型:',
                                        fileType
                                    );


                                    // =================================================
                                    // 未知文件
                                    // =================================================

                                    if (
                                        fileType === 'unknown'
                                    ) {

                                        // 尝试读取前500字节
                                        try {

                                            const decoder =
                                                new TextDecoder(
                                                    'utf-8'
                                                );

                                            const text =
                                                decoder.decode(
                                                    bytes.slice(
                                                        0,
                                                        500
                                                    )
                                                );


                                            console.log(
                                                '[公文下载] 返回内容前500字节:',
                                                text
                                            );

                                        } catch (e) {

                                            console.log(
                                                '[公文下载] 无法读取返回内容:',
                                                e
                                            );
                                        }


                                        alert(
                                            '下载到的文件格式无法识别。\n\n' +
                                            'HTTP状态:' +
                                            response.status +
                                            '\n' +
                                            '文件大小:' +
                                            blob.size +
                                            ' bytes\n\n' +
                                            '请打开 F12 → 控制台查看详细信息。'
                                        );


                                        button.textContent =
                                            '⬇ 下载公文';

                                        button.style.background =
                                            '#1677ff';

                                        return;
                                    }


                                    // =================================================
                                    // 确定文件扩展名
                                    // =================================================

                                    let extension = '';


                                    if (
                                        fileType === 'pdf'
                                    ) {

                                        extension =
                                            '.pdf';

                                    }
                                    else if (
                                        fileType === 'ofd'
                                    ) {

                                        extension =
                                            '.ofd';

                                    }
                                    else if (
                                        fileType === 'doc'
                                    ) {

                                        extension =
                                            '.doc';

                                    }
                                    else if (
                                        fileType === 'docx'
                                    ) {

                                        extension =
                                            '.docx';

                                    }
                                    else if (
                                        fileType === 'xls'
                                    ) {

                                        extension =
                                            '.xls';

                                    }
                                    else if (
                                        fileType === 'xlsx'
                                    ) {

                                        extension =
                                            '.xlsx';

                                    }
                                    else if (
                                        fileType === 'ppt'
                                    ) {

                                        extension =
                                            '.ppt';

                                    }
                                    else if (
                                        fileType === 'pptx'
                                    ) {

                                        extension =
                                            '.pptx';

                                    }
                                    else if (
                                        fileType === 'rtf'
                                    ) {

                                        extension =
                                            '.rtf';
                                    }


                                    const fileName =
                                        title +
                                        extension;


                                    console.log(
                                        '[公文下载] 文件类型:',
                                        fileType.toUpperCase()
                                    );


                                    console.log(
                                        '[公文下载] 最终文件名:',
                                        fileName
                                    );


                                    // =================================================
                                    // 浏览器下载
                                    // =================================================

                                    const blobUrl =
                                        URL.createObjectURL(
                                            blob
                                        );


                                    const a =
                                        document.createElement(
                                            'a'
                                        );


                                    a.href =
                                        blobUrl;

                                    a.download =
                                        fileName;


                                    document.body.appendChild(
                                        a
                                    );


                                    a.click();


                                    a.remove();


                                    setTimeout(
                                        function () {

                                            URL.revokeObjectURL(
                                                blobUrl
                                            );

                                        },
                                        10000
                                    );


                                    // =================================================
                                    // 完成提示
                                    // =================================================

                                    button.textContent =
                                        '✓ ' +
                                        fileType.toUpperCase() +
                                        '下载完成';

                                    button.style.background =
                                        '#52c41a';


                                    setTimeout(
                                        function () {

                                            button.textContent =
                                                '⬇ 下载公文';

                                            button.style.background =
                                                '#1677ff';

                                        },
                                        2000
                                    );

                                };


                            reader.readAsArrayBuffer(
                                blob
                            );

                        },


                    // -------------------------------------
                    // 失败
                    // -------------------------------------

                    onerror:
                        function (error) {

                            console.error(
                                '[公文下载] 请求失败:',
                                error
                            );


                            alert(
                                '下载请求失败,请查看浏览器控制台。'
                            );


                            button.textContent =
                                '⬇ 下载公文';

                            button.style.background =
                                '#1677ff';
                        }

                });

            }
        );


        // =================================================
        // 添加按钮
        // =================================================

        document.body.appendChild(
            button
        );


        console.log(
            '[公文下载] 下载按钮已显示'
        );
    }


    // =====================================================
    // 8095 Reader
    // =====================================================

    function init8095() {

        console.log(
            '[公文下载] 进入 8095 Reader'
        );


        const fileUrl =
            getFileUrl();


        if (!fileUrl) {

            console.log(
                '[公文下载] 当前页面没有 file 参数'
            );

            return;
        }


        console.log(
            '[公文下载] 文件地址已找到'
        );


        function tryCreateButton() {

            if (!document.body) {
                return false;
            }


            createDownloadButton(
                fileUrl
            );


            return true;
        }


        // 页面已经有 body
        if (
            tryCreateButton()
        ) {

            return;
        }


        // body 还没出现
        const observer =
            new MutationObserver(
                function () {

                    if (
                        tryCreateButton()
                    ) {

                        observer.disconnect();
                    }

                }
            );


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


    // =====================================================
    // 根据端口判断页面
    // =====================================================

    if (
        location.port === '8600'
    ) {

        init8600();

    }
    else if (
        location.port === '8095'
    ) {

        init8095();
    }


})();

四、脚本说明

本文提供的“公文一键下载”脚本,是根据“我”目前使用的OA系统编写的,可以完美适配。对于其他OA系统来说,是一个通用的模板,但不是可以直接适用于所有OA系统的脚本。

如果其他系统工作方式比较类似,通常只需要重点检查以下几个地方:

(一)修改 OA 网站地址

脚本:

// @match        http://192.171.40.3:8600/*
// @match        http://192.171.40.3:8095/*

这里表示脚本在哪些网页上运行。

其中:

  • 8600 是当前 OA 收文单页面;
  • 8095 是 WebOffice 公文阅读页面。

如果其他 OA 使用的是不同的 IP、域名或端口,需要将这里修改成实际地址。

(二)修改文件服务器地址

脚本:

// @connect      192.171.40.3

这一项与公文实际下载地址有关。

本例中,OA 页面和公文文件都位于 192.171.40.3 服务器,因此使用这个地址。

如果其他 OA 的公文文件存放在另外的服务器,例如:

OA:   192.168.1.10
文件: 192.168.1.20

那么就需要根据实际的公文下载地址修改为:

// @connect      192.168.1.10
// @connect      192.168.1.20

(三)确认“标题”字段

标题字段用于文件下载时候的命名。

本脚本会先打开 OA 的收文单,从收文单中的“标题”字段获取公文名称,然后将这个名称作为下载后的 OFD/PDF 文件名。

当前 OA 中,标题字段对应:goa_doc_receivefile_title

本例中,通过开发人员工具可以看到标题字段对应的 id 为 goa_doc_receivefile_title,因此脚本可以通过 #goa_doc_receivefile_title 对其进行定位。如果换成其他 OA,“标题”字段的 HTML 结构很可能不同。

这时需要使用 Edge 浏览器的“开发人员工具”检查收文单中的标题字段。打开收文单后,按 F12 进入开发人员工具,使用“元素”工具定位“标题”对应的内容,查看这个字段在网页 HTML 中的具体结构,例如 id、name、class 或其他能够唯一定位该字段的属性。然后根据实际找到的结构,对脚本中的 findReceiveTitle() 函数进行相应修改,使脚本能够准确找到标题内容。

需要注意的是,标题获取失败并不会影响公文正文的下载。 如果没有成功获取标题,脚本会使用默认文件名“公文正文”进行保存。


(四)确认公文实际下载地址

1.本文中的OA分析

很多 OA 打开公文时,浏览器地址栏显示的是 WebOffice 阅读器页面的地址,而不是公文文件本身的下载地址。

例如本文使用的 OA,打开公文正文后,浏览器访问的是类似下面的地址:http://192.171.40.3:8095/reader?file=http%3A%2F%2F192.171.40.3%3A8600%2Fflexbase%2Fattachment%2Fdl%3Ffid%3D402a91…

这里需要注意,file 是 WebOffice 阅读器地址中的一个参数。

而这个 file 参数的内容,经过 URL 解码以后,才是真正的公文文件地址:http://192.171.40.3:8600/flexbase/attachment/dl?fid=……&chksum=……&hash=……

也就是说,整个过程实际上是:
打开 WebOffice 阅读器→ 读取当前页面 URL→ 找到 file 参数→ 得到 file 参数里面的地址→ 请求这个地址→ 获得真正的 OFD/PDF 文件

脚本中对应的代码就是:

const url = new URL(location.href);
const file = url.searchParams.get('file');

这里的作用并不是直接下载文件,而是从 WebOffice 阅读器地址中取出 file 参数的内容。

取得这个地址以后,脚本再通过:GM_xmlhttpRequest()向真正的公文文件地址发起请求,最终获得 OFD /PDF文件。

2.如果用于其他 OA

如果其他 OA 的 WebOffice 阅读器也是通过类似:?file=xxxx的方式提供公文文件,那么可以采用类似的方法获取下载地址。

如果打开正文后,地址栏中没有 file 参数,也不要认为公文无法下载。这只说明该 OA 获取正文文件的方式不同。

这时可以使用 Edge 浏览器的开发人员工具进行分析:

  • 打开公文正文;
  • 按 F12 打开开发人员工具;
  • 进入 “网络(Network)”;
  • 重新打开公文或者刷新页面;
  • 查看页面加载过程中产生的请求;
  • 找到实际返回 OFD /PDF 文件,或者返回公文文件地址的请求。

需要找到的是最终能够获取 OFD /PDF 文件的地址或接口,而不是单纯的 WebOffice 阅读器页面地址。

简单来说:浏览器地址栏里的 WebOffice 地址只是“阅读页面”,真正需要下载的是它背后的 OFD /PDF 文件。不同 OA 获取这个文件的方式可能不同,因此这一部分通常需要根据实际系统进行适配。

另外,如果文件地址需要登录状态、Cookie 或其他认证信息才能访问,也需要根据实际 OA 的认证方式进行处理。

(五)确认 OA 和 WebOffice 的页面关系

脚本目前按照两个页面进行处理:
8600→ 收文单→ 获取公文标题→ 8095→ WebOffice 阅读器→ 获取 OFD /PDF 地址→ 显示“下载公文”按钮

所以代码最后有这样的判断,用以实现判断当前浏览器打开的是哪个端口,然后决定执行哪一部分功能。

if (location.port === '8600') {
    init8600();
}
else if (location.port === '8095') {
    init8095();
}

如果其他 OA 使用的端口不同,或者 OA 和 WebOffice 根本不是这样的两个页面,就需要根据实际情况进行修改。

(六)小结

因此,如果想把本文的脚本用于其他 OA,并不是简单地修改一个 IP 地址就可以。

建议按照下面的顺序检查:
OA 地址 → 文件服务器 → 收文单标题字段 → WebOffice 实际文件地址 → ⑤OA 与 WebOffice 的页面关系

其中,前两项通常比较容易修改,后三项则需要根据具体 OA 的页面结构进行分析。

如果其他 OA 同样采用“收文单 + WebOffice + OFD /PDF”的工作方式,那么这套思路仍然具有参考价值,只需要针对具体系统进行适配即可。

免责声明

本文及所提供的“公文一键下载”脚本仅用于技术研究、学习以及合法的办公场景。

本脚本的设计前提是:使用者已经通过正常方式登录 OA 系统,并且对相关公文具有相应的查看、使用、下载、打印或保存权限。

本脚本不会提供账号破解、权限提升、绕过身份认证或突破服务器访问控制等功能。脚本仅对用户当前已经能够正常访问的页面进行处理,并将页面中已经提供的公文文件地址获取到本地。

需要特别注意:能够在线查看公文,并不意味着一定具有下载、复制、打印或者对外传播的权限。 实际使用时,应当遵守所在单位的信息安全、保密管理、档案管理以及 OA 系统的相关规定。

对于涉及国家秘密、工作秘密、商业秘密、个人信息或者其他受保护信息的文件,在未确认具有相应权限和保存条件的情况下,请勿使用本工具进行下载、复制、存储或传播。现行《中华人民共和国保守国家秘密法》对国家秘密的复制、记录、存储以及涉密信息系统的管理均有明确要求。

使用者应自行确认其使用行为具有合法性和相应授权。因使用本脚本造成的任何文件泄露、违规传播、违反单位管理制度或其他法律责任,由实际使用者自行承担。本文作者不对使用者未经授权的使用行为承担责任。

如果所在单位明确禁止下载、复制、打印相关公文,或者 OA 系统通过技术措施明确限制上述操作,请遵循单位规定,不应通过本工具规避相关管理要求。

0 评论
最新
最旧 得票最多