2015-10-17 146 views
1

我正在编写一些C#应用程序和一些Google Spreadsheets集成。我处于一种需要将工作表中的某些数据移入不同电子表格的情况。这个工作表包含大量的数据,所以我想避免遍历其内容。如何将现有Google工作表插入Google电子表格?

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_other_title”电子表格“some_worksheet_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

我能想到的是批量更新。 (这意味着应对非常细胞)。或谷歌应用脚​​本有一个命令来做到这一点。您可以通过HTML服务调用Google应用程序脚本。 – eddyparkinson

+0

@eddyparkinson我最初使用批量更新,但结果不可靠。我现在正在使用Apps脚本。我会将它作为回答后发 –

回答

1

在尝试了几种不同的方式之后,最可靠的方法竟然是Google Apps Scripting。一般而言,我的解决方案包含一个Google Apps脚本,该脚本由我的C#应用​​程序通过execution API调用。以下是一些代码示例,演示了所有这些如何一起工作。

因此,这里的谷歌应用程序脚本,从一个工作表移动内容到另一个:

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

... 

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

+1

非常好的答案! – Mogsdad

相关问题