Techioz Blog

Google Apps Script (スプレッドシート) で生成されたファイルを Ruby 経由でダウンロードする

概要

Google スプレッドシートの各タブに基づいて json ファイルを生成する次の Apps Script について考えてみましょう。

function makeJSON() {
  var spreadsheetId = 'someId';
  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheets = spreadsheet.getSheets();
  var urls = []; // Array to store the URLs of the created files

  for (var i =  1; i < sheets.length; i++) {
    var sheet = sheets[i];
    var data = sheet.getRange(3,  1, sheet.getLastRow() -  2, sheet.getLastColumn()).getValues();
    var headers = sheet.getRange(2,  1,  1, sheet.getLastColumn()).getValues()[0];

    var objects = data.map(function(row) {
    var obj = {};
    for (var j =  0; j < headers.length; j++) {
      //process data in the sheets
    return obj;
    });


    var json = JSON.stringify(objects, null,  2);

// Create a blob from the JSON string
var blob = Utilities.newBlob(json, 'application/json', sheet.getName() + '.json');

 var folder = DriveApp.getFolderById('someId');
 var file = folder.createFile(blob);

 file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
 urls.push(file.getUrl());
  }
}

function doGet() {
  var urls = makeJSON(); // Call the function to generate JSON files and get their URLs
  Logger.log(urls) //this logs all the files correctly
  return ContentService.createTextOutput(JSON.stringify({urls: urls})).setMimeType(ContentService.MimeType.JSON);

}

folder.createFile(blob); を使用してすべての json ファイルを Google ドライブにダウンロードすると、正常に動作します。ただし、スクリプトを手動で実行してファイルをダウンロードする必要がないように、Ruby スクリプトを使用してこれを実行しようとしています。そこで、それを Web アプリ (パブリック権限) としてデプロイし、それを以下の web_app_url に置きました。 .rb ファイルは次のとおりです。

 require 'typhoeus'
require 'json'

web_app_url = 'https://script.google.com/macros/s/digits/exec'
download_directory = './downloads'

# Make an HTTP GET request to trigger the Web App
response = Typhoeus.get(web_app_url, followlocation: true)

if response.success?
  json_response = JSON.parse(response.body)
  json_urls = json_response['urls']

  # Download each file to the specified directory
  json_urls.each do |url|
    file_name = File.basename(url)
    download_path = File.join(download_directory, file_name)

    # Use open-uri to open the URL and download the file
    require 'open-uri'
    open(url, 'rb') do |file|
      File.open(download_path, 'wb') do |local_file|
        local_file.write(file.read)
      end
    end
    puts "Downloaded #{file_name} to #{download_directory}"
  end
else
  puts "HTTP status code: #{response.code}"
  puts "Response body: #{response.body}"
  puts "Failed to fetch URLs: #{response.status_message}"
end

しかし、次のエラーが発生します。

    warning: calling URI.open via Kernel#open is deprecated, call URI.open directly or use URI#open
Traceback (most recent call last):
    9: from download.rb:15:in `<main>'
    8: from download.rb:15:in `each'
    7: from download.rb:21:in `block in <main>'
    6: from /Users/me/.rvm/rubies/ruby-2.7.2/lib/ruby/2.7.0/open-uri.rb:19:in `open'
    5: from /Users/me/.rvm/rubies/ruby-2.7.2/lib/ruby/2.7.0/open-uri.rb:50:in `open'
    4: from /Users/me/.rvm/rubies/ruby-2.7.2/lib/ruby/2.7.0/open-uri.rb:744:in `open'
    3: from /Users/me/.rvm/rubies/ruby-2.7.2/lib/ruby/2.7.0/open-uri.rb:178:in `open_uri'
    2: from download.rb:22:in `block (2 levels) in <main>'
    1: from download.rb:22:in `open'
download.rb:22:in `initialize': No such file or directory @ rb_sysopen - ./downloads/view?usp=drivesdk (Errno::ENOENT)

どこが間違っているのか誰か教えてもらえますか?また、これらのファイルをマシン上のどこにダウンロードするかを指定するにはどうすればよいでしょうか?

解決策

表示されているスクリプトから、 Logger.log(urls) // これによりすべてのファイルが正しく記録されると思うと、現在の問題の原因は urls.push(file.getUrl()) の getUrl() にあるのではないかと推測しました。 ;。 getUrl() の場合、URL は https://drive.google.com/file/d//view?usp=drivesdk のようになります。このリンクはファイルへの直接リンクではありません。これが現在の問題の原因である可能性があると思います。私の推測が正しければ、次の修正はどうでしょうか?

この変更では、Ruby 側が変更されます。

json_urls.each do |url|
  file_name = File.basename(url)
json_urls.each do |url|
  id = url.split("/")[5]
  url = "https://drive.usercontent.google.com/download?confirm=xxx&id=#{id}" # This is from https://gist.github.com/tanaikech/f0f2d122e05bf5f971611258c22c110f
  file_name = File.basename(url)

変更したスクリプト全体は以下の通りです。

require 'typhoeus'
require 'json'

web_app_url = 'https://script.google.com/macros/s/{deploymentId}/exec'
download_directory = './downloads'

# Make an HTTP GET request to trigger the Web App
response = Typhoeus.get(web_app_url, followlocation: true)

if response.success?
  json_response = JSON.parse(response.body)
  json_urls = json_response['urls']

  # Download each file to the specified directory
  json_urls.each do |url|
    id = url.split("/")[5]
    url = "https://drive.usercontent.google.com/download?confirm=xxx&id=#{id}"
    file_name = File.basename(url)
    download_path = File.join(download_directory, file_name)

    # Use open-uri to open the URL and download the file
    require 'open-uri'
    open(url, 'rb') do |file|
      File.open(download_path, 'wb') do |local_file|
        local_file.write(file.read)
      end
    end
    puts "Downloaded #{file_name} to #{download_directory}"
  end
else
  puts "HTTP status code: #{response.code}"
  puts "Response body: #{response.body}"
  puts "Failed to fetch URLs: #{response.status_message}"
end