雨下個不停阿,但是待在家裡感覺會廢掉,還是出門尋找咖啡廳...
早上還是要寫作業,先整理一下dependency,把xmlrun發佈到npm,不然node/npm降版會有問題...
接下來開始繼續寫單元測試~~只寫了一半,就要吃午飯了...OK,下午繼續
下午看了一下文件,確認從OPCUA Server讀取資料節點的方式,看到不少東西...node-opcua官方文件很缺乏說明文字,反而在github上的issue track裡面有不少人貢獻code snippet XD,沒有仔細google還不會找到。知道找東西的方向,以後就方便了(可以寫整合測試?)
晚上回頭比較一下pptx viewer的資料,看起來前幾天調整過的xml to json的剖析器產出的格式大致上沒錯,現在需要的是把東西匯總起來成為單一的json。另外,我發現以前手賤,改了轉換後的名稱...tag名稱有sld的會改成slide,r:id的會改成rid之類,這個可能直接去調整viewer比較好。
彙整要怎麼做呢?處理的大致上都是ppt/底下的檔案...副檔名是.rels的把他的副檔名去掉當作key,內容則是從xml轉出的東西。副檔名是.xml的,則把presentation.xml的內容匯總到presentation這個key下面,其他的則依照ppt之下的目錄名來當作key,xml檔名當作下一層的key,然後把轉換後的json物件指派給這個key就可以。
媒體的部分會需要另外處理,今天先不管他。首先加一行:
let data = {relations:{},presentation:{},themes:{},slideMasters:{},slideLayouts:{},slides:{}};
.rels的資訊彙整到relations裡面,ppt/presentation.xml的資訊彙整到presentation,其他則根據目錄及xml檔名各自處理。程式改成:
<!DOCTYPE html>
<html>
<head>
    <title>file reader</title>
    <style>
        .dropable {
            width: 99%;
            height: 100px;
            background-color: #369;
            color: white;
            border: solid 3px gray;
            border-radius: 5px;
            padding: 5px 5px 5px 5px;
        }
        .message {
            width: 99%;
            background-color: #ddd;
            border: solid 1px gray;
            border-radius: 5px;
            overflow: auto;
        }
        .preview {
            font-size: 10px;
        }
    </style>
</head>
<body>
    <div id="target" class="dropable">
        <select id="encoding">
            <option value="utf-8">utf-8</option>
            <option value="big5">big5</option>
            <option value="gb2312">gb2312</option>
            <option value="shift-jis">shift-jis</option>
        </select>
        <input type="file" id="file" />
        <div id="console"></div>
    </div>
    <div id="panel" class="message"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pako/1.0.11/pako_inflate.min.js"></script>
    <script src="lib/zipfs.js"></script>
    <script src="node_modules/xmlrun/index.js"></script>
    <script>
        var _target = document.getElementById('target');
        var _message = document.getElementById('panel');
        var _file = document.getElementById('file');
        _file.onchange = function(e) {
            clearlog();
            if (this.files.length > 0) {
                var reader = new FileReader();
                reader.onload = (e) => {
                    var buffer = e.target.result;
                    log('file size: ' + buffer.byteLength);
                    let encoding = document.getElementById('encoding').value;
                    let start = new Date().getTime();
                    zipfs(buffer, pako.inflateRaw, encoding, (err, files) => {
                        let data = {relations:{},presentation:{},themes:{},slideMasters:{},slideLayouts:{},slides:{}};
                        if (!!err) return console.log(err);
                        log(`in callback: ${files.length} files extracted.`);
                        log(`${(new Date().getTime()-start)} ms`);
                        let str = '<table border="1" cellspacing="0" cellpadding="5" width="99%"><tr><th>file name</th><th>buffer length</th><th>last modified date</th><th>last modified time</th><th>compressed size</th><th>uncompressed size</th></tr>';
                        files.forEach(file => {
                            str += `<tr><td>${file.file_name}</td><td>${file.content.length}</td><td>${zipfs.formatMSDOSDate(file.last_modified_date)}</td><td>${zipfs.formatMSDOSTime(file.last_modified_time)}</td><td>${file.compressed_size}</td><td>${file.uncompressed_size}</td></tr>`;
                            str += `<tr><td colspan="6" class="preview" style="overflow-x:hidden">`;
                            if (checkExt(file.file_name, '.rels') && file.file_name.length > 5) {
                                let runner = xmlnode(zipfs.uintToString(file.content));
                                runner.setRunner('default', function(target) {
                                    if(!!target.child) {
                                        return target.child.reduce((acc, cur) => {
                                            Object.assign(acc, cur.run());
                                            return acc;
                                        }, {})
                                    }
                                    else return {};
                                });
                                runner.setRunner('Relationship', target => {
                                    let result = {};
                                    result[target.attr['Id']] = target.attr['Target'];
                                    result[target.attr['Target']] = target.attr['Id'];
                                    return result;
                                });
                                data.relations[file.file_name.substr(0, file.file_name.length-5)] = runner.run();
                            }
                            if (checkExt(file.file_name, '.xml')) {
                                let runner = xmlnode(zipfs.uintToString(file.content));
                                runner.setRunner('default', function(target) {
                                    let result = {};
                                    if(!!target.attr) {
                                        result = Object.keys(target.attr).reduce((pre, cur) => {
                                            pre[cur] = target.attr[cur];
                                            return pre;
                                        }, {});
                                    }
                                    if(!!target.val) result._value = target.val;
                                    if(!!target.child && Array.isArray(target.child) && target.child.length > 0) {
                                        target.child.forEach(c => {
                                            if(!result[c.tag]) {
                                                result[c.tag] = c.run();
                                            } else {
                                                if(Array.isArray(result[c.tag])) {
                                                    result[c.tag].push(c.run());
                                                } else {
                                                    let _t = result[c.tag];
                                                    result[c.tag] = [];
                                                    result[c.tag].push(_t);
                                                    result[c.tag].push(c.run());
                                                }
                                            }
                                        });
                                    }
                                    return result;
                                });
                                if(file.file_name === 'ppt/presentation.xml') {
                                    data.presentation = runner.run();
                                } else {
                                    let paths = file.file_name.split('/');
                                    if(Object.keys(data).indexOf(paths[1]) > -1) {
                                        data[paths[1]][file.file_name] = runner.run();
                                    }
                                }
                            }
                            if (checkExt(file.file_name, '.jpeg') || checkExt(file.file_name, '.jpg')) {
                                let im = new Image();
                                im.src = `data:image/jpeg;base64,${zipfs.arrayBufferToBase64(file.content)}`;
                                if(im.width > 1024) {
                                    str += `<img width="1024px" src="data:image/jpeg;base64,${zipfs.arrayBufferToBase64(file.content)}">`;
                                } else {
                                    str += `<img src="data:image/jpeg;base64,${zipfs.arrayBufferToBase64(file.content)}">`;
                                }
                            }
                            if (checkExt(file.file_name, '.png')) {
                                let im = new Image();
                                im.src = `data:image/png;base64,${zipfs.arrayBufferToBase64(file.content)}`;
                                if(im.width > 1024) {
                                    str += `<img width="1024px" src="data:image/png;base64,${zipfs.arrayBufferToBase64(file.content)}">`;
                                } else {
                                    str += `<img src="data:image/png;base64,${zipfs.arrayBufferToBase64(file.content)}">`;
                                }
                            }
                            if (checkExt(file.file_name, '.mp3')) {
                                str += '<audio controls type="audio/mpeg" src="data:audio/mp3;base64,' +
                                    zipfs.arrayBufferToBase64(file.content) + '">';
                            }
                            str += '</td></tr>';
                        });
                        str += `<tr><td colspan="6" class="preview" style="overflow-x:hidden"><pre>${JSON.stringify(data, null, 2)}</pre></td></tr>`;
                        str += '</table>';
                        document.getElementById('panel').innerHTML = str;
                    });
                };
                reader.readAsArrayBuffer(this.files[0]);
            }
            function checkExt(name, ext) {
                return name.lastIndexOf(ext) === name.length - ext.length;
            }
            function log(msg) {
                document.getElementById('console').innerHTML += `<br />${msg}`; 
            }
            function clearlog() {
                document.getElementById('console').innerHTML = ''; 
            }
        }
    </script>
</body>
</html>
.rels及.xml就不顯示內容,而匯總到最後才顯示匯總的結果。網頁一開始:

後面顯示匯總的json:



其實仔細觀察,裡面還存在一些細微的差異,另外,themes底下的檔案不知道為什麼沒有匯總進來,這些就留到明天吧。
謝謝分享!連續潮濕的天氣,濕氣確實令人會感到昏倦、特別累,記得定期除濕開抽濕機,屆時編程都寫得更精目、清晰,加油呀!