我正在编写一个带有一些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;
}
我希望能够获取要添加到电子表格的工作表,然后将其添加到电子表格中。这是行不通的。上面的代码在目标电子表格中创建(正确标题)工作表,但不传输工作表的任何内容。
有没有办法让它正确传输内容?
答案 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;
}
...
以上两段代码在一起使用时,完美地解决了这个问题。不同数据量之间的执行时间差异不大,并且传输中没有数据损坏。