質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

PDF

PDF(Portable Document Format)とはISOによって国際標準として制定されている電子ドキュメント用の拡張子です。

Q&A

解決済

1回答

8893閲覧

【GAS】PDF作成時のエラーコード

Yam_Fu

総合スコア12

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

PDF

PDF(Portable Document Format)とはISOによって国際標準として制定されている電子ドキュメント用の拡張子です。

0グッド

0クリップ

投稿2021/04/22 13:23

PDFを自動で複数作成し、結合した状態で指定のフォルダに格納するスクリプトをネットの情報をもとに独自アレンジを加えて作成。
いざ実行をさせた際に以下エラーが発生し、試行錯誤しましたが解決できず息詰まってしまい質問をさせていただきました。

<実現したいこと>
スプレッドシートの複数作成しているシートをそれぞれPDFにして仮フォルダに格納。
その後仮フォルダに格納されたPDFを1枚に結合して確定版のフォルダに格納。その際にフォルダ名も変更。
その後仮フォルダに保存されている複数のPDFを削除。

<その際のエラーメッセージ>
Exception: Request failed for https://docs.google.com returned code 400. Truncated server response: <!DOCTYPE html><html lang="ja"><head><meta name="description" content="ウェブ ワープロ、プレゼンテーション、スプレッドシート"><meta name="viewport" content="width=device-wid... (use muteHttpExceptions option to examine full response)

現在のところエラーとなっている以下の部分の対応を検証中ですがこれといって成果がなく・・・。
var blob = UrlFetchApp.fetch(url, options).getBlob().setName(name); //PDFを作成する

また前後部分も含めて調べてはいるものの根本的に間違っている可能性もありますが今のスキルレベルではなかなか特定ができず、
ご教示いただけますと幸いです。

よろしくお願い致します。

GAS

1function CreatePdf(){ //当該スプレッドシートをPDFに変換して指定のフォルダに保存するScript 2 3 var folderId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //一時保管用のフォルダIDを定義 4 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなスプレッドシートを取得 5 var sheet = ss.getSheetByName(sheetName2); //スプレッドシートの名前を取得 6 var LastRow = sheet.getRange(6, 29, 205, 1).getValues().filter(String).length; //空白の要素を除いた取得指定のスプレッドシートの長さを取得 7 var SheetIds = sheet.getRange(6, 29, LastRow, 1).getValues(); //指定範囲の値(シートID)を取得 8 var ssId = ss.getId(); //アクティブなスプレッドシートのIDを取得 9 var date = new Date(); //今日の日時情報を取得 10 var dateString = Utilities.formatDate(date, "Asia/Tokyo", "yyyyMMddHHmmss"); //日時情報を文字列に変換 11 12//表紙の6〜i行目に記載されたSheetIdに基づきPDFを生成する。 13 for (var i = 6; i <= SheetIds.length + 6; i++) { //「6」は繰り返しスタートの行数分をプラス 14 var shId = sheet.getRange(i, 29).getValue(); //指定シートの指定範囲の値を取得 15 var name = i + ".pdf"; //保存ファイルの名前を形式指定 16 var token = ScriptApp.getOAuthToken(); //アクセストークンを取得しAPIリクエストを送信する 17 var baseUrl = "https://docs.google.com/spreadsheets/d/" + ssId + "/export?gid=" + shId; //スプレッドシートをPDF出力するためのURLを設定 18 var pdfOptions //PDF出力のオプションを設定(URLの組み立て) 19 = "&exportFormat=pdf&format=pdf" //ファイル形式の指定 pdf / csv / xls / xlsx 20 + "&size=A4" //用紙サイズ (A4) 21 + "&portrait=true" //用紙の向き true: 縦向き / false: 横向き 22 + "&fitw=true" //ページ幅を用紙にフィットさせるか true: フィットさせる / false: 原寸大 23 + "&top_margin=0.50" //上の余白を設定 24 + "&right_margin=0.50" //右の余白を設定 25 + "&bottom_margin=0.50" //下の余白を設定 26 + "&left_margin=0.50" //左の余白を設定 27 + "&horizontal_alignment=CENTER" //水平方向の位置を設定 28 + "&vertical_alignment=TOP" //垂直方向の位置を設定 29 + "&printtitle=false" //スプレッドシート名の表示有無 trueで表示あり、falseで表示なし(記述方法 "&sheetnames=true") 30 + "&sheetnames=false" //シート名の表示有無 trueで表示あり、falseで表示なし(記述方法 "&sheetnames==true") 31 + "&note=false" //??? 32 var url = baseUrl + pdfOptions; //PDFを作成するためのURL 33 var options = {headers: {'Authorization': 'Bearer ' + token}}; //headersにアクセストークンを格納する 34 var blob = UrlFetchApp.fetch(url, options).getBlob().setName(name); //PDFを作成する 35 var folder = DriveApp.getFolderById(folderId); //PDFの保存先フォルダを指定 36 folder.createFile(blob) //PDFを指定したフォルダに保存する 37 Logger.log(i) // 38 var staySecond = 2 //「秒数」を設定(PDFにする処理がHTTP(S)の通信を必要とするため以下の処理が必要) 39 Utilities.sleep(staySecond * 1000); //意図しないエラーを防ぐため一時的に処理を遅らせる処理を設定(時間がミリ秒単位のため1000掛ける) 40 } 41 42//一時保管フォルダのファイル一覧を取得して、1つのPDFに統合する。 43 var iteratorList = folder.getFiles(); //「folder」に対して全てのファイルを取得する 44 var pdfList = []; //リスト用の空の一次元配列を作成 45 var pattern = /.*.pdf$/; //拡張子(ファイルのタイプのこと:「.xlsx」「.pdf」など)の指定(正規表現) 46 while (iteratorList.hasNext()){ //「iteratorList」に対して一時保管フォルダにまだ取り出していないファイルが存在するかを判定 47 var file = iteratorList.next(); //「iteratorList」に対して反復処理でまだ取り出していないファイルがあれば「file」に格納 48 if (pattern.test(file.getName())){ //ファイルの拡張子(ファイルのタイプのこと:「.xlsx」「.pdf」など)をチェック 49 pdfList.push(file); //一次元配列に格納 50 } 51 } 52 var mergedFileName = FileName + dateString + ".pdf"; //結合後のPDFファイルのファイル名を指定 53 mergedFile = mergePdfs(folder, mergedFileName, pdfList); //PDFの結合を実行 54 a = merge 55 Logger.log("PDFs merged") 56 57//一時保管フォルダから統合したPDFを検索して、指定フォルダに移動させる。 58 var files = folder.getFilesByName(mergedFileName); //「folder」に対して全ての結合後のPDFファイルのファイル名を「files」に格納        59 while (files.hasNext()) { //「filse」に対して一時保管フォルダにまだ取り出していないファイルが存在するかを判定 60 var file = files.next(); //「iteratorList」に対して反復処理でまだ取り出していないファイルがあれば「file」に格納 61 var fileName = file.getName(); //「file」に対して格納されている全てのファイル名を「fileName」に格納 62 console.log(fileName); 63 } 64 var storage = DriveApp.getFolderById('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); //結合後のPDFファイルを格納するフォルダIDを設定 65 file.moveTo(storage); //「file」に対して作成したファイルを指定先のフォルダに移動させる 66 Logger.log("Merged PDF moved") 67 68//一時保管フォルダに残された個別PDFを削除する。 69 var individualFiles = folder.getFiles(); //「folder」に対して全てのファイル名を取得 70 while(individualFiles.hasNext()){       //「individualFiles」に対してまだ取り出していないファイルが存在するかを判定 71 var file = individualFiles.next(); //「individualFiles」に対して反復処理でまだ取り出していないファイルがあれば「file」に格納 72 file.setTrashed(true); //「file」に対してファイルを削除 73 } 74 Logger.log("Individual files trashed") 75 76//おめでとう 77 Browser.msgBox("指定のフォルダにPDFが出力されました") // 78 79}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

var shId = sheet.getRange(i, 29).getValue();

の部分で取得しているシートIDが誤っている(存在しないシートIDを指定している)可能性があります。

下記のコード例(色つきの追加部分)では、
指定されたシートIDが存在しない場合、そのシートのPDF出力をスキップし
シートIDが書かれているセルの右隣に「シート不存在」と書き込み
次のシートIDの処理に移ります。


追記 

※修正前の回答コードではシートIDの存在確認が正常にできていなかったため、
冒頭にexistSheetId(spreadsheet, sheetId)関数を追加しております。

あと、動作確認の際にいくつか不具合を見つけたので修正しています。
赤色[行頭がマイナス記号]の行 -> 削除
緑色[行頭がプラス記号]の行 -> 追加

mergePdfs関数については、コメントに記載の通り、https://qiita.com/mat_aaa/items/d77320769b5ac837a98b に記載のコードを参照してください。 
(長すぎて回答欄に書けないため)

diff

1 2// シートIDの存在を確認する関数。 3// 指定したspreadsheetにsheetIdで指定したIDのシートが存在するか確認する。 4// 引数:spreadsheet:対象のスプレッドシート。sheetId:確認対象のシートID 5// 返値:存在する場合true, 存在しない場合false。 6+function existSheetId(spreadsheet, sheetId){ 7+ var sheets = spreadsheet.getSheets(); 8+ for(var i = 0; i < sheets.length; i++){ 9+ if(sheets[i].getSheetId() === sheetId) return true; // 「===」にしないと、sheetIdが空白の場合trueとなってしまう。 10+ } 11+ return false; 12+} 13 14function CreatePdf(){ //当該スプレッドシートをPDFに変換して指定のフォルダに保存するScript 15 16 var folderId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //一時保管用のフォルダIDを定義 17 var ss = SpreadsheetApp.getActiveSpreadsheet(); //アクティブなスプレッドシートを取得 18 var sheet = ss.getSheetByName(sheetName2); //スプレッドシートの名前を取得 19 var LastRow = sheet.getRange(6, 29, 205, 1).getValues().filter(String).length; //空白の要素を除いた取得指定のスプレッドシートの長さを取得 20- var SheetIds = sheet.getRange(6, 29, LastRow, 1).getValues(); //指定範囲の値(シートID)を取得 21+ // LastRowに入っているのは、最終行番号ではなく、シートIDの要素数なので、6を足す必要あり。(途中に空白のセルがない前提ならば本当は5(6-1)でよい。) 22+ var SheetIds = sheet.getRange(6, 29, LastRow + 6, 1).getValues(); //指定範囲の値(シートID)を取得 23 var ssId = ss.getId(); //アクティブなスプレッドシートのIDを取得 24 var date = new Date(); //今日の日時情報を取得 25 var dateString = Utilities.formatDate(date, "Asia/Tokyo", "yyyyMMddHHmmss"); //日時情報を文字列に変換 26 27+ var tempFileNames = [] // 一時ファイルのファイル名格納用の配列。 28 //表紙の6〜i行目に記載されたSheetIdに基づきPDFを生成する。 29 for (var i = 6; i <= SheetIds.length + 6; i++) { //「6」は繰り返しスタートの行数分をプラス 30 var shId = sheet.getRange(i, 29).getValue(); //指定シートの指定範囲の値を取得 31+ if (existSheetId(ss, shId) == false){ //シートIDが存在するかどうか判定 32+ Logger.log(`シートID:${shId}(${i}行目)は存在しません。`); 33+ sheet.getRange(i, 30).setValue("シートID不存在"); //シートIDが存在しない場合右隣りのセルに書き込む 34+ continue; 35+ } 36 37 var name = i + ".pdf"; //保存ファイルの名前を形式指定 38+ tempFileNames.push(name) // 一時ファイル名を格納する。 39 var token = ScriptApp.getOAuthToken(); //アクセストークンを取得しAPIリクエストを送信する 40 var baseUrl = "https://docs.google.com/spreadsheets/d/" + ssId + "/export?gid=" + shId; //スプレッドシートをPDF出力するためのURLを設定 41 var pdfOptions //PDF出力のオプションを設定(URLの組み立て) 42 = "&exportFormat=pdf&format=pdf" //ファイル形式の指定 pdf / csv / xls / xlsx 43 + "&size=A4" //用紙サイズ (A4) 44 + "&portrait=true" //用紙の向き true: 縦向き / false: 横向き 45 + "&fitw=true" //ページ幅を用紙にフィットさせるか true: フィットさせる / false: 原寸大 46 + "&top_margin=0.50" //上の余白を設定 47 + "&right_margin=0.50" //右の余白を設定 48 + "&bottom_margin=0.50" //下の余白を設定 49 + "&left_margin=0.50" //左の余白を設定 50 + "&horizontal_alignment=CENTER" //水平方向の位置を設定 51 + "&vertical_alignment=TOP" //垂直方向の位置を設定 52 + "&printtitle=false" //スプレッドシート名の表示有無 trueで表示あり、falseで表示なし(記述方法 "&sheetnames=true") 53 + "&sheetnames=false" //シート名の表示有無 trueで表示あり、falseで表示なし(記述方法 "&sheetnames==true") 54 + "&note=false" //??? 55 var url = baseUrl + pdfOptions; //PDFを作成するためのURL 56 var options = {headers: {'Authorization': 'Bearer ' + token}}; //headersにアクセストークンを格納する 57 var blob = UrlFetchApp.fetch(url, options).getBlob().setName(name); //PDFを作成する 58 var folder = DriveApp.getFolderById(folderId); //PDFの保存先フォルダを指定 59 folder.createFile(blob) //PDFを指定したフォルダに保存する 60 Logger.log(i) // 61 var staySecond = 2 //「秒数」を設定(PDFにする処理がHTTP(S)の通信を必要とするため以下の処理が必要) 62 Utilities.sleep(staySecond * 1000); //意図しないエラーを防ぐため一時的に処理を遅らせる処理を設定(時間がミリ秒単位のため1000掛ける) 63 } 64 65 //一時保管フォルダのファイル一覧を取得して、1つのPDFに統合する。 66- var iteratorList = folder.getFiles(); //「folder」に対して全てのファイルを取得する 67- var pdfList = []; //リスト用の空の一次元配列を作成 68- var pattern = /.*.pdf$/; //拡張子(ファイルのタイプのこと:「.xlsx」「.pdf」など)の指定(正規表現) 69- while (iteratorList.hasNext()){ //「iteratorList」に対して一時保管フォルダにまだ取り出していないファイルが存在するかを判定 70- var file = iteratorList.next(); //「iteratorList」に対して反復処理でまだ取り出していないファイルがあれば「file」に格納 71- if (pattern.test(file.getName())){ //ファイルの拡張子(ファイルのタイプのこと:「.xlsx」「.pdf」など)をチェック 72- pdfList.push(file); //一次元配列に格納 73- } 74- } 75 76+// getFilesだと、作成日時が新しい順に取得してしまい、統合後のページの順番が逆になるため 77+// 一時ファイル名を格納した配列を利用して、出力順に格納する。 78+ var pdfList = []; 79+ for(var i = 0; i < tempFileNames.length; i++){ 80+ pdfList.push(folder.getFilesByName(tempFileNames[i]).next()); 81+ } 82 83 var mergedFileName = FileName + dateString + ".pdf"; //結合後のPDFファイルのファイル名を指定 84 mergedFile = mergePdfs(folder, mergedFileName, pdfList); //PDFの結合を実行 85- a = merge // 不要なので削除。 86 Logger.log("PDFs merged") 87 88 //一時保管フォルダから統合したPDFを検索して、指定フォルダに移動させる。 89 var files = folder.getFilesByName(mergedFileName); //「folder」に対して全ての結合後のPDFファイルのファイル名を「files」に格納        90 while (files.hasNext()) { //「filse」に対して一時保管フォルダにまだ取り出していないファイルが存在するかを判定 91 var file = files.next(); 92 var fileName = file.getName(); //「file」に対して格納されている全てのファイル名を「fileName」に格納 93 console.log(fileName); 94 } 95 var storage = DriveApp.getFolderById("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); //結合後のPDFファイルを格納するフォルダIDを設定 96 file.moveTo(storage); //「file」に対して作成したファイルを指定先のフォルダに移動させる 97 Logger.log("Merged PDF moved") 98 99 //一時保管フォルダに残された個別PDFを削除する。 100 var individualFiles = folder.getFiles(); //「folder」に対して全てのファイル名を取得 101 while(individualFiles.hasNext()){       //「individualFiles」に対してまだ取り出していないファイルが存在するかを判定 102 var file = individualFiles.next(); //「individualFiles」に対して反復処理でまだ取り出していないファイルがあれば「file」に格納 103 file.setTrashed(true); //「file」に対してファイルを削除 104 } 105 Logger.log("Individual files trashed") 106 107 //おめでとう 108 Browser.msgBox("指定のフォルダにPDFが出力されました") // 109 110 } 111 112function mergePdfs(directory, name, pdf_list) { 113114https://qiita.com/mat_aaa/items/d77320769b5ac837a98b に記載のコードを参照のこと。 115}

投稿2021/04/22 15:16

編集2021/04/24 06:28
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yam_Fu

2021/04/23 12:16

ご指摘ありがとうございます。 確かにLogger.log(shId);で確認したところ参照先のIDと異なる?IDが表示されました。 例えばシート入力値が「123456」に対してLogger.log(shId)での表示で「1.23456E」となってしまっています。 ただ異なる点としては小数点の違いだけでその先のPDF同志の結合ができていませんが一様個別単体のPDFは作成できているようです。 やはりこちらはエラー原因でしょうか? また解決策としては何か思い当たる点はありますでしょうか?
退会済みユーザー

退会済みユーザー

2021/04/23 14:00 編集

>シート入力値が「123456」に対してLogger.log(shId)での表示で「1.23456E」となってしまっています。 Loggerで変数に入った数字を単体を表示させた場合、小数ありで表示されますが、シート上で小数が入っていなければ、URLに渡されているシートIDも表示された通り(小数なし)のはずです なので、(セルに記入されているIDが本当にスプレッドシート上に存在する限りは)ログ上で「1.23546E」と表示されていることは特にエラーの原因ではないと思います。 (質問に記載されているエラーの原因は、「シートの29列目に記入したシートIDがスプレッドシート上に存在しないこと」です) > ただ異なる点としては小数点の違いだけでその先のPDF同志の結合ができていませんが一様個別単体のPDFは作成できているようです。 おそらく、存在しないシートIDに当たるまでは、正常に個別シートの内容がPDF出力されたのだと思います。 存在しないシートIDに突き当たった時点で、質問文にある「Exception: Request failed ~」のエラーが発生し、プログラムが停止し、結合処理まで行かなかったのだと思われます。 回答欄に、指定したシートIDが存在しない場合のエラー処理の例を追加しましたので試してみていただけると幸いです。
Yam_Fu

2021/04/24 01:36

補足含め丁寧なご説明ありがとうございます。 シートIDの小数点に関する問題については理解しました。 確認したところおっしゃる通りIDが存在しない範囲を参照していた関係でエラーとなっていたようです。 エラーの場合の処理コードもありがとうございます。大変助かりました。 またその後今度は以下のエラーコードが発生してしまいました。 ReferenceError: mergePdfs is not defined 今回はmergePdfsが定義されていないエラーとなっておりますが定義されてないとはどういった状態なのでしょうか? 現在はとりあえずmergePdfsの引数に問題がないか確認しておりますが何か気になる点がありましたらアドバイスなど頂けないでしょうか? 何度もお伺いしてしまい申し訳ございません。
退会済みユーザー

退会済みユーザー

2021/04/24 06:32 編集

全体のコードを修正しました。 「ReferenceError: mergePdfs is not defined」のエラーが出る原因は、 mergePdfs という関数がどこにも書かれていないか、 mergePdfs の関数名が誤っている可能性があります。 仮に、mergePdfs関数を書いていない場合: mergePdfs関数は、https://qiita.com/mat_aaa/items/d77320769b5ac837a98b の「▼こういう感じのコードになります。」の左の三角マークをクリックするとコードが表示されるため、それをコピペしてください。 (なお、このコードを実行するには、メニューの「実行 」→「ChromeV8を利用した新しいランタイム~」が「有効」になっている必要があります。)
Yam_Fu

2021/04/25 12:07

ご教示ありがとうございます。 またその他動作不具合のコードに関しても修正・補足をしていただきありがとうございます。 mergePdfsについてもその後、いただいたコメントを読み返していくうちになんとなく理解してきました。 アドバイスをいただきました内容にて改修したところうまく動作することを確認しました。 改めてこの度は大変ありがとうございました。 ここまで補足・ご教示いただきましたこと、大変感謝しております。 今回のコードについてもしっかりと理解し自分のものとしていきたいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問