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

时间:2015-10-17 19:03:09

标签: c# google-spreadsheet-api

我正在编写一个带有一些Google Spreadsheets集成的C#应用​​程序。我的情况是工作表中有一些数据需要移动到不同的电子表格中。此工作表包含大量数据,因此我希望避免迭代其内容。

API指南提供了an example如何在电子表格中创建新工作表。我修改了它以将现有工作表添加到电子表格中:

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);
        }
    }
}

为清楚起见,上面的代码尝试将“some_worksheet_title”工作表放在“some_other_title”电子表格中,并将其放入“some_title”电子表格中。以下是上述代码中引用的函数。

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;

    }   

我希望能够获取要添加到电子表格的工作表,然后将其添加到电子表格中。这是行不通的。上面的代码在目标电子表格中创建(正确标题)工作表,但不传输工作表的任何内容。

有没有办法让它正确传输内容?

1 个答案:

答案 0 :(得分:1)

尝试了几种不同的方法后,最可靠的方法是Google Apps Scripting。一般而言,我的解决方案涉及我的C#应用​​程序通过execution API调用的Google Apps脚本。下面是一些代码示例,演示了所有这些如何协同工作。

所以这是将内容从一个工作表移动到另一个工作表的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";
}

这是我最终使用的代码的严格剥离版本,这就是为什么有两个电子表格ID字段。这意味着两个工作表是否在同一个电子表格中并不重要。

解决方案的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;
    }

...

以上两段代码在一起使用时,完美地解决了这个问题。不同数据量之间的执行时间差异不大,并且传输中没有数据损坏。