1

要求に応じてPDFファイルを操作するFirebase機能を作成しました。いくつかの操作を行い、ストレージに保存してデータベースにハッシュをアーカイブします。Firebase機能で、同期/非同期Messをきれいにします。ES6

私はそれをうまく動作させることができましたが、ES6以前のコールバックで作業する方法を実際には学んだことはありません。私はこのすべてに慣れていないし、矢印の機能と約束を働くことを学んだ。しかし、ここで私は純粋なjavascriptとその作業を何とかしているパッケージを使用する必要がありますが、本当にこの同期/非同期の混乱をきれいに処理して、関数を処理した後にfirebaseへの約束を返す必要があります。

私の関数には、pdfライブラリ用に別のファイルを用意するための奇妙なファイルハンドリングも含まれています: たとえば、QMPコードを作成してtmpファイルに保存し、別のライブラリを使用してpngのjpgを作成しますtmpに再度保存します。私は何かよりスマートなことをすることができれば、何か示唆やヒントに触れることができます。

機能がまだ実行中に数ミリ秒で完了しているため、チェーンに何か問題があります。

私は自分のコードにいくつかのコメントを追加しましたが、私はES6に変更する手がかりがなく、この大きな混乱を取り除く手助けができれば非常に感謝しています。

const hummus = require('hummus'); 
const request = require('request'); 
const fs = require('fs'); 
const sha1 = require('sha1'); 
const images = require("images"); 

exports.handleDocSignRequests = functions.database.ref('/user_writeable/docrequests/{uid}').onWrite(event => { 
    var userReq = event.data.val(); 
    var userRef = event.data.ref; 
    if (!userReq) return Promise.resolve(); 
    if (!userReq.docpath) return Promise.resolve(); 
    let uid = event.params.uid; 
    let filename = userReq.docpath; // File to sign and hash 

    return bucket.file(filename).getSignedUrl({ // getting downloadurl from Firebase Storage 
     action: 'read' 
    }).then(
     (downloadpath) => { 
      downloadpath = downloadpath[0]; 
      //download pdf - how to turn this into a promise? 
      download = function (uri, filename, callback) { 
       request.head(uri, function (err, res, body) { 
        request(uri).pipe(fs.createWriteStream(filename)).on('close', callback); 
       }); 
      }; 
      let pdfsourse = LOCAL_TMP_FOLDER + 'downloadedfile.pdf'; 
      return download(downloadpath, pdfsourse, function() { // download callback turn this into ES6 
       console.log('download finished'); 
       let qrjpg = LOCAL_TMP_FOLDER + 'qrcode.jpg'; 
       var qrpng = LOCAL_TMP_FOLDER + 'qrcode.png'; 
       let qrurl = 'https://some.url/' + userReq.docid; 
       let pdfdest = LOCAL_TMP_FOLDER + 'newpdf.pdf'; 
       let logfile = './hummus.log'; 

       QRCode.toFile(qrpng, qrurl, { // how to make this a part of the "chain" and go on when finished 
        version: 4, type: "png", scale: 2, margin: 0 
       }, function (err) { 
        if (err) throw err; 
        console.log('qr png ready'); 
        images(qrpng).save(qrjpg, {operation: 100}); // save png as jpg 
        console.log('qr jpg ready'); 
       }); 


       // Doing stuff to PDF with HummusJs 
       let pdfWriter = hummus.createWriterToModify(pdfsourse, { 
        modifiedFilePath: pdfdest, 
        log: logfile, 
        userPassword: 'user', 
        ownerPassword: 'owner', 
        userProtectionFlag: 4 

       }); 
       let pdfReader = pdfWriter.getModifiedFileParser(); 
       let arialFont = pdfWriter.getFontForFile(ariallocal); 
       let textOptions = {font: arialFont, size: 5, colorspace: 'gray', color: 0x00}; 

       for (let i = 0; i < pdfReader.getPagesCount(); ++i) { 
        let pageBox = pdfReader.parsePage(i).getMediaBox(); 
        let pageModifier = new hummus.PDFPageModifier(pdfWriter, i, true); 
        let ctx = pageModifier.startContext().getContext(); 
        ctx.writeText('Document saved', 5, 110, textOptions); 
        ctx.drawImage(5, 52, qrfile, 
         { 
          transformation: { 
           width: 40, 
           height: 40, 
           fit: 'always' 
          } 
         }); 
        pageModifier.endContext().writePage(); 
       } 
       pdfWriter.end(); 
       // How can I be sure PDF is done and written to tmp file? Or is this given by sync function? 
       // Reading finished PDF from file again, to get base64 for hashing - is there a better way? 
       let newpdf = fs.readFileSync(pdfdest); 
       let base64pdf = newpdf.toString('base64'); 
       let hash = sha1(base64pdf); 
       let signobj = {}; 
       signobj['hash'] = hash; 
       // Check if document already in database, if not write hash to database, 
       // upload finished pdf to original place and archive 
       // and return remove request 
       let sign_ref = docsign_ref.child(userReq.docid); 
       return sign_ref.once('value').then(function (snap) { 
        if (!snap.val()) { //Document is new 
         let upload1 = bucket.upload(destcry, {destination: filename}).then 
         (suc => { 
          console.log('uploaded'); 
         }); 
         // 
         let upload2 = bucket.upload(destcry, {destination: 'signed/' + userReq.docid + '.pdf'}).then 
         (suc => { 
          console.log('uploaded'); 
         }); 
         return Promise.all([upload1, upload2]).then(// When both uploads are finished go on 
          (suc) => { 
           return sign_ref.set(signobj).then(
            (suc) => { 
             // Remove Request and return Promise 
             return userRef.remove(); 
            }); 

          }); 
        } 
        else { 
         //Document already in database, this should never happen, only for seq reasons 
         console.log('doc already in database); 
         return Promise.resolve(); 
        } 
       }); 
      }); 
     }); 
}); 
+0

は、私の機能が働いている、判明います時々起こった。それでもこの機能は永遠に続く。私のローカルノードでは、envは約3秒間実行され、クラウド機能では約2分かかります。 – jbb

+0

ここではあまりにも多くの質問があります:(1)パフォーマンスを改善する方法? (2)PDFをtmpファイルに書き込んで書き込む方法を教えてください。 (3)「同期/非同期の混乱を取り除く」方法(4)よりスマートなファイル処理方法1つの質問に集中し、もっと質問があれば新しい質問を投稿してください。あなたが今質問を持っている方法はあまりにも広すぎます。 – trincot

答えて

0

私がコメントに書いたように、あなたの質問は広すぎます。あなたがタイトルに書いているように質問に焦点を当てます。約束にはいくつかのコールバックベースのスキーム

  • ターン、および
  • は、ネストされたthen通話
を避けて、約束のチェーンをフラット化:

それは、約束をES6になる主な改良点は、あなたがしなければならないということです

最後に、外部関数を呼び出すときには、必ずそれを約束として扱います。非同期の処理が完了したら、そのパターンに固執する必要があります。だから、このようなあなたの主な機能を呼び出します。(!)ここで

handleDocSignRequests().then(....); 

は、上記の2点を解決するために、あなたのコードのいくつかの未テスト書き直したものです。主な変更点は ***です。そこにいくつかのミスかもしれませんが、パターンが明確でなければなりません:「エラー:ECONNRESETを書く」私は処分した約束を再試行の私のファイルのアップロードをラップした後

const hummus = require('hummus'); 
const request = require('request'); 
const fs = require('fs'); 
const sha1 = require('sha1'); 
const images = require("images"); 

exports.handleDocSignRequests = functions.database.ref('/user_writeable/docrequests/{uid}').onWrite(event => { 
    var userReq = event.data.val(); 
    var userRef = event.data.ref; 
    if (!userReq) return Promise.resolve(); 
    if (!userReq.docpath) return Promise.resolve(); 
    let uid = event.params.uid; 
    let filename = userReq.docpath; // File to sign and hash 

    // *** Define these variables at this level, so they are accessible throughout 
    //  the flattened promise-chain: 
    let signobj = {}; 
    let sign_ref = docsign_ref.child(userReq.docid); 

    return bucket.file(filename).getSignedUrl({ // getting downloadurl from Firebase Storage 
     action: 'read' 
    }).then((downloadpath) => { 
     downloadpath = downloadpath[0]; 
     let pdfsourse = LOCAL_TMP_FOLDER + 'downloadedfile.pdf'; 
     // *** turned into promise: 
     // *** return the promise to the main promise chain 
     return new Promise(resolve => { 
      request.head(downloadpath, function (err, res, body) { 
       request(downloadpath) 
        .pipe(fs.createWriteStream(pdfsourse)) 
        .on('close', resolve); 
      }); 
     }); 
    }).then(function() { 
     console.log('download finished'); 
     let qrjpg = LOCAL_TMP_FOLDER + 'qrcode.jpg'; 
     var qrpng = LOCAL_TMP_FOLDER + 'qrcode.png'; 
     let qrurl = 'https://some.url/' + userReq.docid; 
     // *** Return a promise to make this a part of the "chain" and go on when finished 
     return new Promise((resolve, reject) => { 
      QRCode.toFile(qrpng, qrurl, { 
       version: 4, type: "png", scale: 2, margin: 0 
      }, function (err) { 
       if (err) reject(err); // *** reject 
       console.log('qr png ready'); 
       images(qrpng).save(qrjpg, {operation: 100}); // save png as jpg 
       console.log('qr jpg ready'); 
       resolve(); // *** resolve the promise now it's done 
      }); 
     }); 
    }).then(function() { 
     // Doing stuff to PDF with HummusJs 
     let pdfdest = LOCAL_TMP_FOLDER + 'newpdf.pdf'; 
     let logfile = './hummus.log'; 
     let pdfWriter = hummus.createWriterToModify(pdfsourse, { 
      modifiedFilePath: pdfdest, 
      log: logfile, 
      userPassword: 'user', 
      ownerPassword: 'owner', 
      userProtectionFlag: 4 
     }); 
     let pdfReader = pdfWriter.getModifiedFileParser(); 
     let arialFont = pdfWriter.getFontForFile(ariallocal); 
     let textOptions = {font: arialFont, size: 5, colorspace: 'gray', color: 0x00}; 

     for (let i = 0; i < pdfReader.getPagesCount(); ++i) { 
      let pageBox = pdfReader.parsePage(i).getMediaBox(); 
      let pageModifier = new hummus.PDFPageModifier(pdfWriter, i, true); 
      let ctx = pageModifier.startContext().getContext(); 
      ctx.writeText('Document saved', 5, 110, textOptions); 
      ctx.drawImage(5, 52, qrfile, { 
       transformation: { 
        width: 40, 
        height: 40, 
        fit: 'always' 
       } 
      }); 
      pageModifier.endContext().writePage(); 
     } 
     pdfWriter.end(); 
     // How can I be sure PDF is done and written to tmp file? Or is this given by sync function? 
     // Reading finished PDF from file again, to get base64 for hashing - is there a better way? 
     let newpdf = fs.readFileSync(pdfdest); 
     let base64pdf = newpdf.toString('base64'); 
     let hash = sha1(base64pdf); 
     signobj['hash'] = hash; 
     // Check if document already in database, if not write hash to database, 
     // upload finished pdf to original place and archive 
     // and return remove request 
     // *** Only return the basic promise. Perform the `then` on the outer chain. 
     return sign_ref.once('value'); 
    }).then(function (snap) { 
     // *** Do simple case first: 
     if (snap.val()) { 
      //Document already in database, this should never happen, only for seq reasons 
      console.log('doc already in database); 
      return Promise.resolve(); 
     } // *** No need for `else` here. The above `return` is enough 
     //Document is new 
     // *** use array to replace two blocks of very similar code 
     let promises = [filename, 'signed/' + userReq.docid + '.pdf'].map(destination => 
      return bucket.upload(destcry, {destination}).then(suc => { 
       console.log('uploaded'); 
      }); 
     }); 
     // *** Return the basic promise. Perform the `then` on the outer chain 
     return Promise.all(promises);// Wait for both uploads to finish 
    }).then(suc => { 
     // *** I suppose this also has to run when not a new document.... 
     // *** Again: return the promise and perform the `then` on the outer chain 
     return sign_ref.set(signobj); 
    }).then(suc => { 
     // Remove Request and return Promise 
     return userRef.remove(); 
    }); 
}); 
+0

本当に、私はすぐに多くの質問をしました。しかし、あなたの提案した方法で私の機能を書き直して、これらの多くを解決したことが判明しました。その外観は本当にきれいで簡単です。また、すべての作業が完了した後、意図された方法で約10秒で完了します。ありがとうございました – jbb