如何通过word-js替换海量内容控件中的文字?

How replace the text in a huge number of content controls via word-js?

我正在尝试编写一个函数,它将富文本内容控件列表和单个字符串作为参数,并用该字符串替换所有匹配内容控件的内容。

虽然这适用于较少量的内容控件,但它无法处理包含大量内容控件的文档。我必须处理包含 700 多个内容控件和单独标题的文档。在这种情况下,代码仅替换前 66X CC,然后中止并抛出 GeneralException。我认为这只是由于大量的内容控制。当我尝试为所有这些 CC (GeneralException) 注册绑定时,我遇到了类似的问题。但这是另一个话题。

我试图解决这个问题,方法是限制每个 .sync() 的更改量并循环 CC,根据需要执行尽可能多的循环。但是,由于 office-js 的异步性质,这并不容易。到目前为止,我对 javascript-async-promise-programming 不是很熟悉。但这就是我想出的:

function replaceCCtextWithSingleString (CCtitleList, string) {
    var maxPerBatch = 100;

    /*
     * A first .then() block is executed to get proxy objects for all selected CCs
     *
     * Then we would replace all the text-contents in one single .then() block. BUT:
     * Word throws a GeneralException if you try to replace the text in more then 6XX CCs in one .then() block.
     * In consequence we only process maxPerBatch CCs per .then() block
     */
    Word.run(function (context) {
        var CCcList = [];

        // load CCs
        for(var i = 0; i < CCtitleList.length; i++) {
            CCcList.push(context.document.contentControls.getByTitle(CCtitleList[i]).load('id'));
        }

        return context.sync().then(function () { // synchronous
            var CClist = [];
            // aggregate list of CCs
            for(var i = 0; i < CCcList.length; i++) {
                if(CCcList[i].items.length == 0) {
                    throw 'Could not find CC with title "'+CCtitleList[j]+'"';
                }
                else {
                    CClist = CClist.concat(CCcList[i].items);
                }
            }
            $('#status').html('Found '+CClist.length+' CCs matching the criteria. Started replacing...');
            console.log('Found '+CClist.length+' CCs matching the criteria. Started replacing...');

            // start replacing
            return context.sync().then((function loop (replaceCounter, CClist) {
                // asynchronous recoursive loop
                for(var i = 0; replaceCounter < CClist.length && i < maxPerBatch; i++) { // loop in loop (i does only appear in condition)
                    // do this maxPerBatch times and then .sync() as long as there are still unreplaced CCs
                    CClist[replaceCounter].insertText(string, 'Replace');
                    replaceCounter++;
                }

                if(replaceCounter < CClist.length) return context.sync()  // continue loop
                    .then(function () {
                        $('#status').html('...replaced the content of '+replaceCounter+' CCs...');
                        return loop(replaceCounter, numCCs);
                    });
                else  return context.sync() // end loop
                    .then(function () {
                        $('#status').html('Replaced the content of all CCs');
                    });
            })(0, CClist));
        });
    }).catch(function (error) {
        $('#status').html('<pre>Error: ' + JSON.stringify(error, null, 4) + '</pre>');
        console.log('Error: ' + JSON.stringify(error, null, 4));
        if (error instanceof OfficeExtension.Error) {
            console.log('Debug info: ' + JSON.stringify(error.debugInfo, null, 4));
        }
        throw error;
    });
}

但是...它不起作用。它替换前 100 个 CC,然后停止。没有失败,没有例外或任何事情。 return loop(replaceCounter, CClist); 只是没有执行,我不知道为什么。如果我尝试在调试器中进入这一行,它会将我抛到 office-js 代码中的某处。

有什么建议吗?

编辑:

我根据 Juan Balmori 的建议更新了我的代码,它非常有效:

    function replaceCCtextWithSingleString_v1_1 (CCtitleList, string) {
    Word.run(function (context) {
        var time1 = Date.now();

        // load the title of all content controls
        var CCc = context.document.contentControls.load('title');

        return context.sync().then(function () { // synchronous
            // extract CC titles
            var documentCCtitleList = [];
            for(var i = 0; i < CCc.items.length; i++) { documentCCtitleList.push(CCc.items[i].title); }

            // check for missing titles and replace
            for(var i = 0; i < CCtitleList.length; i++) {
                var index = documentCCtitleList.indexOf(CCtitleList[i]);
                if(index == -1) { // title is missing
                    throw 'Could not find CC with title "'+CCtitleList[i]+'"';
                }
                else { // replace
                    CCc.items[index].insertText(string, 'Replace');
                }
            }

            $('#status').html('...replacing...');

            return context.sync().then(function () {
                var time2 = Date.now();
                var tdiff = time2-time1;
                $('#status').html('Successfully replaced all selected CCs in '+tdiff+' ms');
            });
        });
    }).catch(function (error) {
        $('#status').html('<pre>Error: ' + JSON.stringify(error, null, 4) + '</pre>');
        console.log('Error: ' + JSON.stringify(error, null, 4));
        if (error instanceof OfficeExtension.Error) {
            console.log('Debug info: ' + JSON.stringify(error.debugInfo, null, 4));
        }
    });
}

它仍然需要 13995 毫秒才能完成,但至少它有效:-)

有什么想法吗?是什么引发了 GeneralException?

我发布了一个关于速度问题的新问题:What is the fastest way of replacing the text of many content controls via office-js?

问得好.. 很久以前我做了一些性能测试,我能够更改文档中超过 10k 的内容控件。有700你应该没问题。 不确定你为什么要预先填写一个列表,这是不需要的,你实际上是在浏览 2 倍的集合,这对性能不利。您可以在遍历集合时进行字符串比较!

这是一个例子,我刚刚用一个假设标签为 "test" 的 700 内容控制文档做了一个快速测试。

我能够 1. 将他们的文本与你想要比较的任何内容进行比较(它是一个字符串) 2.如果条件为真,则更改值。

完成操作用了 5134 毫秒,这是代码。我觉得还是可以接受的。

希望对您有所帮助!

 function perfContentControls() {
        var time1 = Date.now(); // lets see in how much time we complete the operation :)
        var CCs =0
        Word.run(function (context) {
            var myCCs = context.document.body.contentControls.getByTag("test");
            context.load(myCCs);
            return context.sync()
            .then(function () {
                CCs  = myCCs.items.length
                for (var i = 0; i < CCs; i++) {
                    if (myCCs.items[i].text == "new text 3") // you can check the cc content and if needed replace it....
                         myCCs.items[i].insertText("new text 4", "replace");
                
                }

                return context.sync()
                .then(function () {
                    var time2 = Date.now();
                  var diff = time2 - time1;
                  console.log("# of CCs:" + CCs + " time to change:" + diff + "ms");
                })
            })
           .catch(function (er) {
               console.log(er.message);

           })

        })

    }