简体   繁体   English

如何将现有的Google工作表插入Google电子表格?

[英]How can I insert an existing Google Worksheet into a Google Spreadsheet?

I'm writing a C# app with some Google Spreadsheets integration. 我正在编写具有某些Google Spreadsheets集成的C#应用​​程序。 I'm in a situation where I have some data in a worksheet that needs to be moved into a different spreadsheet. 我处于工作表中有一些数据需要移到其他电子表格中的情况。 This worksheet contains a huge amount of data, so I want to avoid iterating through its contents. 该工作表包含大量数据,因此我想避免遍历其内容。

The API guide gives an example of how to create a new worksheet within a spreadsheet. API指南提供有关如何在电子表格中创建新工作表的示例 I modified it to add an existing worksheet to the spreadsheet: 我对其进行了修改,以将现有工作表添加到电子表格中:

using System;
using Google.GData.Client;
using Google.GData.Spreadsheets;

namespace MySpreadsheetIntegration
{
    class Program
    {
        static void Main(string[] args)
        {
            SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");

            SpreadsheetEntry destinationSpreadsheet = fetchGoogleSpreadSheetEntry(service, "some_title");
            SpreadsheetEntry originSpreadsheet = fetchGoogleSpreadSheetEntry(service, "some_other_title");

            // Create a local representation of the new worksheet.
            WorksheetEntry originWorksheet = fetchGoogleWorksheet( originSpreadsheet, "some_worksheet_title" );

            // Send the local representation of the worksheet to the API for
            // creation.  The URL to use here is the worksheet feed URL of our
            // spreadsheet.
            WorksheetFeed wsFeed = destinationSpreadsheet.Worksheets;
            service.Insert(wsFeed, originWorksheet);
        }
    }
}

For clarity, the above code attempts to take the "some_worksheet_title" worksheet in the "some_other_title" spreadsheet, and put it into the "some_title" spreadsheet. 为了清楚起见,以上代码尝试将“ some_other_title”电子表格中的“ some_worksheet_title”工作表放入“ some_title”电子表格中。 Below are the functions referenced in the above code. 以下是以上代码中引用的功能。

public static WorksheetEntry fetchGoogleWorksheet( SpreadsheetEntry spreadsheet, string worksheet_title )
{
    WorksheetFeed wsFeed = spreadsheet.Worksheets;
    WorksheetEntry worksheet = null;

    foreach (WorksheetEntry entry in wsFeed.Entries)
    {
        worksheet = entry;
        if (entry.Title.Text == worksheet_title)
        {
            Console.WriteLine(DateTime.Now.ToString("HH:mm") + ": Worksheet found on Google Drive.");
            break;
        }
    }

    if (worksheet.Title.Text != worksheet_title)
    {
        return null;
    }

    return worksheet;
}

public static SpreadsheetEntry fetchGoogleSpreadSheetEntry( SpreadsheetsService service, string spreadsheet_title )
{
    Console.WriteLine(DateTime.Now.ToString("HH:mm") + ": Looking for spreadsheet on Google Drive.");
    SpreadsheetQuery query = new SpreadsheetQuery();
    SpreadsheetFeed feed;

    feed = service.Query(query);

    SpreadsheetEntry spreadsheet = null;
    // Iterate through all of the spreadsheets returned
    foreach (SpreadsheetEntry entry in feed.Entries)
    {
        // Print the title of this spreadsheet to the screen
        spreadsheet = entry;
        if (entry.Title.Text == spreadsheet_title)
        {
            Console.WriteLine(DateTime.Now.ToString("HH:mm") + ": Spreadsheet found on Google Drive.");
            Console.WriteLine(DateTime.Now.ToString("HH:mm") + ": Looking for worksheet in spreadsheet.");
            break;
        }
    }

    if (spreadsheet.Title.Text != spreadsheet_title)
    {
        return null;
    }
    return spreadsheet;

    }   

I expected to be able to fetch to worksheet I want to add to the spreadsheet, and just add it to the spreadsheet. 我希望能够获取要添加到电子表格中的工作表,然后将其添加到电子表格中。 It does not work. 这是行不通的。 The above code creates a (correctly titled) worksheet in the destination spreadsheet, but does not transfer any of the content of the worksheet. 上面的代码在目标电子表格中创建了一个(正确标题)工作表,但没有传输工作表的任何内容。

Is there any way to have it transfer the content correctly? 有什么办法可以正确传输内容?

After trying a few different ways of doing this, the most reliable way turned out to be Google Apps Scripting . 在尝试了几种不同的方法之后,最可靠的方法就是Google Apps脚本 In general terms, my solution involves a Google Apps script that is being called by my C# application via the execution API . 一般而言,我的解决方案涉及一个Google Apps脚本,该脚本由C#应用程序通过执行API调用 Below are some code samples demonstrating how all of this works together. 下面是一些代码示例,展示了所有这些如何协同工作。

So here's the Google Apps script that moves content from one worksheet to another: 因此,这是将内容从一个工作表移动到另一个工作表的Google Apps脚本:

function copyWorksheet( destinationSpreadsheetId, destinationWorksheetTitle, originSpreadsheetId, originWorksheetTitle ) {

  // Spreadsheet where new data will go:
  var dss = SpreadsheetApp.openById(destinationSpreadsheetId);

  // Spreadsheet where new data is coming from:
  var oss = SpreadsheetApp.openById(originSpreadsheetId);

  // Worksheet containing new data:
  var dataOriginWorksheet = oss.getSheetByName(originWorksheetTitle);

  // Worksheet whose data will be 'overwritten':
  var expiredWorksheet = dss.getSheetByName(destinationWorksheetTitle);

  // If a spreadsheet only has one worksheet, deleting that worksheet causes an error.
  // Thus we need to know whether the expired worksheet is the only worksheet in it's parent spreadsheet.
  var expiredWorksheetIsAlone = dss.getNumSheets() == 1 && expiredWorksheet != null;

  // Delete the expired worksheet if there are other worksheets:
  if (expiredWorksheet != null && !expiredWorksheetIsAlone)
    dss.deleteSheet(expiredWorksheet);

  // Otherwise, rename it to something guaranteed not to clash with the new sheet's title:
  if(expiredWorksheetIsAlone)
    expiredWorksheet.setName(dataOriginWorksheet.getName() + destinationWorksheetTitle);

  // Copy the new data into it's rightful place, and give it it's rightful name.
  dataOriginWorksheet.copyTo(dss).setName(destinationWorksheetTitle);

  // Since there are now definitely 2 worksheets, it's safe to delete the expired one.
  if(expiredWorksheetIsAlone)
    dss.deleteSheet(expiredWorksheet);

  // Make sure our changes are applied ASAP:
  SpreadsheetApp.flush();

  return "finished";
}

This is a severely stripped down version of the code I ended up using, which is why there are two spreadsheet ID fields. 这是我最终使用的代码的精简版本,这就是为什么有两个电子表格ID字段的原因。 This means that it does not matter whether or not the the two worksheets are in the same spreadsheet. 这意味着两个工作表是否在同一电子表格中并不重要。

The C# part of the solution looks like this: 解决方案的C#部分如下所示:

// We need these for the method below
using Google.Apis.Script.v1;
using Google.Apis.Script.v1.Data;

...    

public static bool copyWorksheet(ScriptService scriptService, string destinationSpreadsheetId, string destinationWorksheetTitle, string originSpreadsheetId, string originWorksheetTitle)
  {
    // You can get the script ID by going to the script in the 
    // Google Apps Scripts Editor > Publish > Deploy as API executable... > API ID
    string scriptId = "your-apps-script-id";

    ExecutionRequest request = new ExecutionRequest();
    request.Function = "copyWorksheet";
    IList<object> parameters = new List<object>();

    parameters.Add(destinationSpreadsheetId);
    parameters.Add(destinationWorksheetTitle);
    parameters.Add(originSpreadsheetId);
    parameters.Add(originWorksheetTitle);            

    request.Parameters = parameters;

    ScriptsResource.RunRequest runReq = scriptService.Scripts.Run(request, scriptId);

    try
    {
      Operation op = runReq.Execute();

      if (op.Error != null)
      {
        Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " The Apps script encountered an error");
        // The API executed, but the script returned an error.

        IDictionary<string, object> error = op.Error.Details[0];
        Console.WriteLine( "Script error message: {0}", error["errorMessage"]);
        if ( error.ContainsKey("scriptStackTraceElements") )
        {

          // There may not be a stacktrace if the script didn't
          // start executing.
          Console.WriteLine("Script error stacktrace:");
          Newtonsoft.Json.Linq.JArray st = (Newtonsoft.Json.Linq.JArray)error["scriptStackTraceElements"];
          foreach (var trace in st)
            {
              Console.WriteLine(
                "\t{0}: {1}",
                trace["function"],
                trace["lineNumber"]);
            }

          }
        }
        else
        {
          // The result provided by the API needs to be cast into
          // the correct type, based upon what types the Apps
          // Script function returns. Here, the function returns
          // an Apps Script Object with String keys and values.
          // It is most convenient to cast the return value as a JSON
          // JObject (folderSet).

          return true;

        }
      }
      catch (Google.GoogleApiException e)
      {
        // The API encountered a problem before the script
        // started executing.
        Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " Could not call Apps Script");
      }

      return false;
    }

...

The above 2 pieces of code, when used together, solved the problem perfectly. 上面的两段代码一起使用,可以完美地解决该问题。 Execution time does not differ greatly between different volumes of data, and there has been no data corruption with transfers. 不同数据量之间的执行时间没有太大差异,并且传输过程中也没有数据损坏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM