从 PowerPoint 或 Word 相关外接程序获取整个文档

可以创建 Office 外接程序,以将 PowerPoint 演示文稿或 Word 文档发送到远程位置或将其发布到远程位置。 本文演示如何为 PowerPoint 或 Word 生成一个简单的任务窗格加载项,该外接程序将所有演示文稿或文档作为数据对象获取,并通过 HTTP 请求将该数据发送到 Web 服务器。

创建 PowerPoint 或 Word 外接程序的先决条件

本文假定您使用文本编辑器创建 PowerPoint 或 Word 任务窗格外接程序。 若要创建任务窗格加载项,必须创建以下文件。

  • 在共享网络文件夹或 Web 服务器上,需要以下文件。

    • HTML 文件 (GetDoc_App.html) ,其中包含用户界面以及指向 JavaScript 文件的链接 (包括 Office.js 和特定于应用程序的 .js 文件) 和级联样式表 (CSS) 文件。

    • JavaScript 文件 (GetDoc_App.js) ,以包含外接程序的编程逻辑。

    • CSS 文件 (Program.css) 包含加载项的样式和格式。

  • 仅外接程序清单文件 ( 加载项GetDoc_App.xml) ,可在共享网络文件夹或外接程序目录中使用。 该清单文件必须指向前面提到的 HTML 文件的位置。

或者,可以使用以下选项之一为 Office 应用程序创建加载项。 无需创建新文件,因为每个所需文件的等效文件可供更新。 例如,Yeoman 生成器选项包括 ./src/taskpane/taskpane.html./src/taskpane/taskpane.js./src/taskpane/taskpane.css./manifest.xml

创建任务窗格加载项需要了解的核心概念

在开始创建 PowerPoint 或 Word 的此外接程序之前,您应知道如何构建 Office 外接程序和使用 HTTP 请求。 本文不讨论如何从 Web 服务器上的 HTTP 请求中解码 Base64 编码的文本。

为外接程序创建清单

Office 外接程序的清单文件提供有关加载项的重要信息:哪些应用程序可以托管加载项、HTML 文件的位置、加载项标题和说明以及许多其他特征。

  1. 在文本编辑器中,将以下代码添加到清单文件中。

    <?xml version="1.0" encoding="utf-8" ?>
    <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:type="TaskPaneApp">
        <Id>[Replace_With_Your_GUID]</Id>
        <Version>1.0</Version>
        <ProviderName>[Provider Name]</ProviderName>
        <DefaultLocale>EN-US</DefaultLocale>
        <DisplayName DefaultValue="Get Doc add-in" />
        <Description DefaultValue="My get PowerPoint or Word document add-in." />
        <IconUrl DefaultValue="http://officeimg.vo.msecnd.net/_layouts/images/general/office_logo.jpg" />
        <SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />
        <Hosts>
            <Host Name="Document" />
            <Host Name="Presentation" />
        </Hosts>
        <DefaultSettings>
            <SourceLocation DefaultValue="[Network location of app]/GetDoc_App.html" />
        </DefaultSettings>
        <Permissions>ReadWriteDocument</Permissions>
    </OfficeApp>
    
  2. 使用 UTF-8 编码将文件另存为 GetDoc_App.xml 网络位置或外接程序目录。

为外接程序创建用户界面

对于加载项的用户界面,可以使用直接写入 到GetDoc_App.html 文件中的 HTML。 外接程序的编程逻辑和功能必须包含在 JavaScript 文件中, (例如 ,GetDoc_App.js) 。

使用以下过程可为该外接程序创建一个包含标题和单个按钮的简单用户界面。

  1. 在文本编辑器的新文件中,添加所选 Office 应用程序的 HTML。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
            <title>Publish presentation</title>
            <link rel="stylesheet" type="text/css" href="Program.css" />
            <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js" type="text/javascript"></script>
            <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
            <script src="GetDoc_App.js"></script>
        </head>
        <body>
            <form>
                <h1>Publish presentation</h1>
                <br />
                <div><input id='submit' type="button" value="Submit" /></div>
                <br />
                <div><h2>Status</h2>
                    <div id="status"></div>
                </div>
            </form>
        </body>
    </html>
    
  2. 使用 UTF-8 编码将文件另存为 GetDoc_App.html 网络位置或 Web 服务器。

    注意

    请确保加载项的 标记包含 脚本 标记,其中包含指向 Office.js 文件的有效链接。

  3. 我们将使用一些 CSS 为加载项提供简单而现代且专业的外观。 使用以下 CSS 可定义外接程序的样式。

    在文本编辑器的新文件中,添加以下 CSS。

    body
    {
        font-family: "Segoe UI Light","Segoe UI",Tahoma,sans-serif;
    }
    h1,h2
    {
        text-decoration-color:#4ec724;
    }
    input [type="submit"], input[type="button"]
    {
        height:24px;
        padding-left:1em;
        padding-right:1em;
        background-color:white;
        border:1px solid grey;
        border-color: #dedfe0 #b9b9b9 #b9b9b9 #dedfe0;
        cursor:pointer;
    }
    
  4. 使用 UTF-8 编码将文件另存为 Program.css ,保存到网络位置或 GetDoc_App.html 文件所在的 Web 服务器。

添加 JavaScript 以获取文档

在外接程序的代码中,Office.initialize 事件的处理程序会向表单上提交按钮的 Click 事件中添加处理程序,并告知用户外接程序准备就绪。

下面的代码示例演示事件的事件处理程序 Office.initialize 以及用于写入状态 div 的帮助程序函数 updateStatus

// The initialize or onReady function is required for all add-ins.
Office.initialize = function (reason) {

    // Checks for the DOM to load using the jQuery ready method.
    $(document).ready(function () {

        // Run sendFile when Submit is clicked.
        $('#submit').on("click", function () {
            sendFile();
        });

        // Update status.
        updateStatus("Ready to send file.");
    });
}

// Create a function for writing to the status div.
function updateStatus(message) {
    var statusInfo = $('#status');
    statusInfo[0].innerHTML += message + "<br/>";
}

在 UI 中选择“ 提交 ”按钮时,外接程序将调用 sendFile 函数,该函数包含对 Document.getFileAsync 方法的调用。 方法 getFileAsync 使用异步模式,类似于 Office JavaScript API 中的其他方法。 它包含一个必需参数 fileType 以及两个可选参数 optionscallback

fileType 参数需要 FileType 枚举中的三个常量之一: Office.FileType.Compressed (“compressed”) 、 Office.FileType.PDF (“pdf”) 或 Office.FileType.Text (“text”) 。 Document.getFileType 备注下列出了每个平台的当前文件类型支持。 当传入 fileType 参数的 Compressed 时,getFileAsync该方法在本地计算机上创建文件的临时副本,将当前文档作为 PowerPoint 演示文稿文件 (*.pptx) 或 Word 文档文件返回 (*.docx) 。

方法 getFileAsyncFile 对象的形式返回对文件的引用。 对象 File 公开以下四个成员。

属性 size 返回 文件中的字节数。 返回 sliceCount 本文稍后讨论的 Slice 对象数 (文件中) 。

使用以下代码,使用 Document.getFileAsync 方法获取当前 PowerPoint 或 Word 文档作为 File 对象,然后调用本地定义的getSlice函数。 请注意,对象 File 、计数器变量和文件中的切片总数在对 的调用 getSlice 中在匿名对象中传递。

// Get all of the content from a PowerPoint or Word document in 100-KB chunks of text.
function sendFile() {
    Office.context.document.getFileAsync("compressed",
        { sliceSize: 100000 },
        function (result) {

            if (result.status === Office.AsyncResultStatus.Succeeded) {

                // Get the File object from the result.
                var myFile = result.value;
                var state = {
                    file: myFile,
                    counter: 0,
                    sliceCount: myFile.sliceCount
                };

                updateStatus("Getting file of " + myFile.size + " bytes");
                getSlice(state);
            } else {
                updateStatus(result.status);
            }
        });
}

本地函数 getSlice 调用 方法以 File.getSliceAsyncFile 对象检索切片。 方法 getSliceAsync 从切片集合返回 对象 Slice 。 它具有两个必需参数: sliceIndexcallbacksliceIndex 参数采用整数作为索引器进入切片集合。 与 Office JavaScript API 中的其他方法一样, getSliceAsync 该方法还采用回调函数作为参数来处理方法调用的结果。

对象 Slice 使你能够访问文件中包含的数据。 除非方法的 options 参数 getFileAsync 中另有指定, Slice 否则对象的大小为 4 MB。 对象 Slice 公开三个属性: sizedataindex。 属性 size 获取切片的大小(以字节为单位)。 属性 index 获取一个整数,表示切片在切片集合中的位置。

// Get a slice from the file and then call sendSlice.
function getSlice(state) {
    state.file.getSliceAsync(state.counter, function (result) {
        if (result.status == Office.AsyncResultStatus.Succeeded) {
            updateStatus("Sending piece " + (state.counter + 1) + " of " + state.sliceCount);
            sendSlice(result.value, state);
        } else {
            updateStatus(result.status);
        }
    });
}

属性 Slice.data 以字节数组的形式返回文件的原始数据。 如果数据采用文本格式(即 XML 或纯文本),则切片包含原始文本。 如果为 的 fileType 参数Document.getFileAsync传入 Office.FileType.Compressed,则切片会以字节数组的形式包含文件的二进制数据。 对于 PowerPoint 或 Word 文件,切片包含字节数组。

您必须实施自己的函数(或使用可用库),将字节数组数据转换为 Base64 编码的字符串。 有关使用 JavaScript 进行 Base64 编码的信息,请参阅 Base64 编码和解码

将数据转换为 Base64 后,可以通过多种方式将数据传输到 Web 服务器,包括作为 HTTP POST 请求的正文。

添加以下代码以将切片发送到 Web 服务。

注意

此代码将 PowerPoint 或 Word 文件发送到多个切片中的 Web 服务器。 Web 服务器或服务必须将每个单独的切片追加到单个文件中,然后将其保存为 .pptx 或 .docx 文件,然后才能对其执行任何操作。

function sendSlice(slice, state) {
    var data = slice.data;

    // If the slice contains data, create an HTTP request.
    if (data) {

        // Encode the slice data, a byte array, as a Base64 string.
        // NOTE: The implementation of myEncodeBase64(input) function isn't
        // included with this example. For information about Base64 encoding with
        // JavaScript, see https://developer.mozilla.org/docs/Web/JavaScript/Base64_encoding_and_decoding.
        var fileData = myEncodeBase64(data);

        // Create a new HTTP request. You need to send the request
        // to a webpage that can receive a post.
        var request = new XMLHttpRequest();

        // Create a handler function to update the status
        // when the request has been sent.
        request.onreadystatechange = function () {
            if (request.readyState == 4) {

                updateStatus("Sent " + slice.size + " bytes.");
                state.counter++;

                if (state.counter < state.sliceCount) {
                    getSlice(state);
                } else {
                    closeFile(state);
                }
            }
        }

        request.open("POST", "[Your receiving page or service]");
        request.setRequestHeader("Slice-Number", slice.index);

        // Send the file as the body of an HTTP POST
        // request to the web server.
        request.send(fileData);
    }
}

顾名思义, File.closeAsync 方法关闭与文档的连接并释放资源。 尽管 Office 外接程序沙盒垃圾回收对文件的范围外引用,但最佳做法仍然是在代码完成文件后显式关闭文件。 方法 closeAsync 具有单个参数 回调,该参数指定要在调用完成时调用的函数。

function closeFile(state) {
    // Close the file when you're done with it.
    state.file.closeAsync(function (result) {

        // If the result returns as a success, the
        // file has been successfully closed.
        if (result.status === Office.AsyncResultStatus.Succeeded) {
            updateStatus("File closed.");
        } else {
            updateStatus("File couldn't be closed.");
        }
    });
}

最终的 JavaScript 文件可能如下所示:

/*
 * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
 * See LICENSE in the project root for license information.
 */

// The initialize or onReady function is required for all add-ins.
Office.initialize = function (reason) {

    // Checks for the DOM to load using the jQuery ready method.
    $(document).ready(function () {

        // Run sendFile when Submit is clicked.
        $('#submit').on("click", function () {
            sendFile();
        });

        // Update status.
        updateStatus("Ready to send file.");
    });
}

// Create a function for writing to the status div.
function updateStatus(message) {
    var statusInfo = $('#status');
    statusInfo[0].innerHTML += message + "<br/>";
}

// Get all of the content from a PowerPoint or Word document in 100-KB chunks of text.
function sendFile() {
    Office.context.document.getFileAsync("compressed",
        { sliceSize: 100000 },
        function (result) {

            if (result.status === Office.AsyncResultStatus.Succeeded) {

                // Get the File object from the result.
                var myFile = result.value;
                var state = {
                    file: myFile,
                    counter: 0,
                    sliceCount: myFile.sliceCount
                };

                updateStatus("Getting file of " + myFile.size + " bytes");
                getSlice(state);
            } else {
                updateStatus(result.status);
            }
        });
}

// Get a slice from the file and then call sendSlice.
function getSlice(state) {
    state.file.getSliceAsync(state.counter, function (result) {
        if (result.status == Office.AsyncResultStatus.Succeeded) {
            updateStatus("Sending piece " + (state.counter + 1) + " of " + state.sliceCount);
            sendSlice(result.value, state);
        } else {
            updateStatus(result.status);
        }
    });
}

function sendSlice(slice, state) {
    var data = slice.data;

    // If the slice contains data, create an HTTP request.
    if (data) {

        // Encode the slice data, a byte array, as a Base64 string.
        // NOTE: The implementation of myEncodeBase64(input) function isn't
        // included with this example. For information about Base64 encoding with
        // JavaScript, see https://developer.mozilla.org/docs/Web/JavaScript/Base64_encoding_and_decoding.
        var fileData = myEncodeBase64(data);

        // Create a new HTTP request. You need to send the request
        // to a webpage that can receive a post.
        var request = new XMLHttpRequest();

        // Create a handler function to update the status
        // when the request has been sent.
        request.onreadystatechange = function () {
            if (request.readyState == 4) {

                updateStatus("Sent " + slice.size + " bytes.");
                state.counter++;

                if (state.counter < state.sliceCount) {
                    getSlice(state);
                } else {
                    closeFile(state);
                }
            }
        }

        request.open("POST", "[Your receiving page or service]");
        request.setRequestHeader("Slice-Number", slice.index);

        // Send the file as the body of an HTTP POST
        // request to the web server.
        request.send(fileData);
    }
}

function closeFile(state) {
    // Close the file when you're done with it.
    state.file.closeAsync(function (result) {

        // If the result returns as a success, the
        // file has been successfully closed.
        if (result.status === Office.AsyncResultStatus.Succeeded) {
            updateStatus("File closed.");
        } else {
            updateStatus("File couldn't be closed.");
        }
    });
}