Google Sheets API v4 Basics - c#

I am trying to get data from google sheets. For example, let's say I have a sheet "test" and I want to get cell a2 from "test", and so I save the contents of cell a2 as a string.
I believe Google Sheets Api v4 (https://developers.google.com/api-client-library/dotnet/apis/sheets/v4) is what I need to use, but I am not sure how to use it.
I have also found similar question posts on stack overflow, but they all seem outdated.
Finally, I have tried this tutorial (https://developers.google.com/sheets/api/quickstart/dotnet#step_2_prepare_the_project) with my c# visual studio, but I get error messages (which I can include if it is helpful).
So, my question is, how do extract data from a google sheet, using the API or some other tool. Thanks for the help, and please notify me if my question needs editing to better fit this community.
edit: So, following the tutorial, I have this code:
using System;
using System.Windows.Forms;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GoogleSheetsTest1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
class Program
{
static string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
static string ApplicationName = "Google Sheets API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/sheets.googleapis.com-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request parameters.
String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms";
String range = "Class Data!A2:E";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(spreadsheetId, range);
// Prints the names and majors of students in a sample spreadsheet:
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
ValueRange response = request.Execute();
IList<IList<Object>> values = response.Values;
if (values != null && values.Count > 0)
{
Console.WriteLine("Name, Major");
foreach (var row in values)
{
// Print columns A and E, which correspond to indices 0 and 4.
Console.WriteLine("{0}, {1}", row[0], row[4]);
}
}
else
{
Console.WriteLine("No data found.");
}
Console.Read();
}
}
}
}
The error message I am getting is:
Error CS0017 Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point.
I realize this is probably a really stupid question and I hate to be asking for help here, but I am not sure how to use the google api code, so any help/explanation (so I can understand what the fix is) would be really appreciated. Thanks

Related

C# call method from different class with 'args' as a parameter

I'm writing a Discord Bot (Discord.net) and I found myself requiring to access some data on a google sheet using their APIs. Being that I thought it would be best to actually separate those two in two different class files I have tried summoning the Main method of the Google APIs into my program (after having renamed it "Sheets") like this in my Program.cs:
using Discord;
using Discord.WebSocket;
using System;
using System.IO;
using System.Threading.Tasks;
namespace WoM_Balance_Bot
{
public class Program
{
public static void Main(string[] args)
{
GoogleAPI GSheet = new GoogleAPI();
GSheet.Sheets();
new Program().MainAsync().GetAwaiter().GetResult();
}
private DiscordSocketClient _client;
public async Task MainAsync()
{
_client = new DiscordSocketClient();
_client.MessageReceived += CommandHandler;
_client.Log += Log;
var token = File.ReadAllText("bot-token.txt");
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
// Block this task until the program is closed.
await Task.Delay(-1);
}.......ecc
I tried writing the parameters to pass here in these parentheses like "string" and "args" but either I get the syntax wrong or I have a very wrong idea about what to pass exactly.
This is the actual content of GoogleAPI.cs, which is the other class file I created that has the Google Sheet APIs:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Util.Store;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace WoM_Balance_Bot
{
public class GoogleAPI
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-dotnet-quickstart.json
private static readonly string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
private static readonly string ApplicationName = "wombankrolls";
public static void Sheets(string[] args)
{
UserCredential credential;
Console.WriteLine("if you read this then it's good");
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request parameters.
String spreadsheetId = "16W56LWqt6wDaYAU5xNdTWCdaY_gkuQyl4CE1lPpUui4";
String range = "Class Data!G163:I";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(spreadsheetId, range);
// Prints the names and majors of students in a sample spreadsheet:
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
ValueRange response = request.Execute();
IList<IList<Object>> values = response.Values;
/*
if (values != null && values.Count > 0)
{
Console.WriteLine("Name, Major");
foreach (var row in values)
{
// Print columns A and E, which correspond to indices 0 and 4.
Console.WriteLine("{0}, {1}", row[0], row[4]);
}
}
else
{
Console.WriteLine("No data found.");
}
Console.Read();
*/
}
}
}
I have modified it from the quickstart given by Google in a way that I thought it made sense but I still get in the end the same error:
There is no argument given that corresponds to the required formal parameter 'args' of 'GoogleAPI.Sheets(string[])'
as the user "David L" wrote in the comments:
As a general rule of thumb, if you do not use an argument, remove it. C# helps enforce this paradigm by throwing a compiler error if your method expects an argument and you do not provide one, which is exactly what is happening here.
It was my bad as I was under the impression of the total opposite during an API implementation. I would like to always target a clean code as a result and keeping stuff that I will not use was my bad. Thank you David!

C# Console can't read google sheets data into list in program

I am working on a console based search tool for "Warrants" in a game I play that searches off of a google sheet from the google sheets API and C#. Originally I made this on python and it worked perfectly but I had a lot of issues distributing my python file so I moved to C#.
The API is calling the data perfectly fine and I am able to present a list of all the data I was seeking on launch, but when I try and save it to list files inside my program I get the following:
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
I also added in a section that tells me the data type I am calling with row[1] and it says (only one "```", had to double to format):
System.Collections.Generic.List``1[System.Object]
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Diagnostics;
namespace WarrantSearchProgram
{
class Program
{
static readonly string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
static readonly string ApplicationName = "WarrantSearchProgram";
static readonly string SpreadsheetId = "SpreadsheetId";
static readonly string sheet = "Imported Data";
static SheetsService service;
//List of Warrant Column Variables... Only nameList is being used for now
public static IList<object> testOBJ;
public static List<object> wtStatus;
public static List<object> wtType;
public static List<object> wtNum;
public static IList<object> nameList;
public static List<object> wtCivName;
public static List<object> wtDOB;
public static List<object> wtAddress;
public static List<object> wtJs;
public static List<object> wtCharges;
public static List<object> wtEvidence;
public static List<object> wtReqOfc;
public static List<object> wtReqOfcNum;
static void Main(string[] args)
{
//Set console color and other general settings
Console.Title = "DOJ Warrant Search Program UNOFFICIAL";
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.Green;
// Initialization of creds and google sheets
GoogleCredential credential;
using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(Scopes);
}
// Create Google Sheets API service.
service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
//First initilization of warrant sheet info, creates and manages variables.
UpdateSheetData();
while (true)
{
// Main repeating text and SEARCH INPUT
Console.WriteLine("-----------------------------------------------");
Console.WriteLine("Please type in a full name to search for warrants.");
Console.WriteLine("Only ACTIVE warrants will be shown.");
Console.WriteLine("Type in a warrant number to show extra info, including evidence, on just that warrant");
Console.WriteLine("-----------------------------------------------");
Console.Write("Search >>> ");
string searchName = Console.ReadLine();
searchName = searchName.ToUpper();
Console.WriteLine();
Console.Beep();
Console.Clear();
}
}
static void UpdateSheetData()
{
var range = $"{sheet}!A:F";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
IList<IList<object>> values = response.Values;
if (values != null && values.Count > 0)
{
foreach (var row in values)
{
// Calls the row (2nd, name) and displays each name in list
Console.WriteLine("{0}", row[1]);
Console.WriteLine(row.GetType().ToString());
// Attempts to build list of names in program ERROR HERE
nameList.Add(row[1]);
}
}
else
{
Console.WriteLine("No data found.");
}
}
}
}
I removed sections of the code that have nothing to do with this so its easier to read...
As you can tell, I tried IList<object>, List<object>, and List<string> at different times and it didn't work for me.
My goal here is to load each column of data into a list that I can then perform searches on, index, and display matching data from other lists. This is all not very difficult to do once I can load the data up into the program and separate it.
error at row 98
As per your comment, at line 98 you are trying to Add a value to the nameList, which may not be initialized. This is why you get the error you do, Object reference not set to an instance of an object. - The nameList wasn't initiated, so you can't call it's internal functions. You might want to instantiate it, anywhere before you call UpdateSheetData, e.g:
nameList = new List<object>();

Get List of AWS S3 Directory Name from .net core application

I am just stuck with aws s3 on my .net core mvc application. I just simply need to input bucket name of s3 then return all of directory name list in this bucket but this simple task i didn't found anywhere on internet. I already tried few solution provided by AWS forum but problem is this absolutely not works at all. Bellow i have provided my controller code also forum link. Actually the issue they told is Amazon.S3.IO and S3DirectoryInfo namespace was removed from .net core so i am failed to follow them as they advised there. Any one can fix my code bellow which will give a list of bucket directory in .net core application?
I am using two nuget package-
AWSSDK.Core and AWSSDK.S3
Forum Link - Amazon.S3.IO not supported in .Net Core anymore?
Controller:
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
public IActionResult Media()
{
string bucketName = "domain33.com";
AmazonS3Client s3Client = new AmazonS3Client("Access_Key_ID", "Secret_Access_Key", RegionEndpoint.USEast1);
var getResponse = s3Client.ListBucketsAsync(new GetObjectRequest
{
BucketName = bucketName
});
var x = getResponse;
return View();
}
You could try using the ListObjectsV2Async method on IAmazonS3 to retrieve a list of all of the existing objects in the bucket based on the AWS's example. Their code is below in case the link dies:
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-s3-developer-guide/blob/master/LICENSE-SAMPLECODE.)
using Amazon.S3;
using Amazon.S3.Model;
using System;
using System.Threading.Tasks;
namespace Amazon.DocSamples.S3
{
class ListObjectsTest
{
private const string bucketName = "*** bucket name ***";
// Specify your bucket region (an example region is shown).
private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2;
private static IAmazonS3 client;
public static void Main()
{
client = new AmazonS3Client(bucketRegion);
ListingObjectsAsync().Wait();
}
static async Task ListingObjectsAsync()
{
try
{
ListObjectsV2Request request = new ListObjectsV2Request
{
BucketName = bucketName,
MaxKeys = 10
};
ListObjectsV2Response response;
do
{
response = await client.ListObjectsV2Async(request);
// Process the response.
foreach (S3Object entry in response.S3Objects)
{
Console.WriteLine("key = {0} size = {1}",
entry.Key, entry.Size);
}
Console.WriteLine("Next Continuation Token: {0}", response.NextContinuationToken);
request.ContinuationToken = response.NextContinuationToken;
} while (response.IsTruncated);
}
catch (AmazonS3Exception amazonS3Exception)
{
Console.WriteLine("S3 error occurred. Exception: " + amazonS3Exception.ToString());
Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.ToString());
Console.ReadKey();
}
}
}
}
Based on that sample, you could do further processing or add the keys to a list of strings for subsequent processing, instead of just writing it the console as their example code does. For instance, you could add each key to a list, and then process that list to calculate the distinct "directories".

Google Sheets Api v4 Quickstart Tutorial -- System.net.http not found even after redirect

I;m currently working on a project that requires me to read and write data from google sheets. Hence, I started with the Google Sheets Api (v4) Quick start Tutorial for C#. I'm using Visual Studio 2017 and have the latest versions of all nuget packages.
I have followed the tutorial step-by-step, but instead of creating a Console Application, I created a Windows Form Application. The code and other steps were followed exactly as written.
When run inside the IDE, the code runs as expected. However, when published and then run outside the IDE, the system cannot find System.Net.HTTP.
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in WindowsFormsApp1.exe
Could not load file or assembly 'System.Net.Http, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.The system cannot find the file specified.
After researching and trying the solutions posted in other similar questions (which did not work) I landed on a StackOverflow post suggest redirecting older version of System.Net.Http to the latest version. However, this did not work.
App.config with redirection code (screenshot)
Here is my current code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.IO;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-dotnet-quickstart.json
static string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
static string ApplicationName = "Google Sheets API .NET Quickstart";
public Form1()
{
InitializeComponent();
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/sheets.googleapis.com-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request parameters.
String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms";
String range = "Class Data!A2:E";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(spreadsheetId, range);
// Prints the names and majors of students in a sample spreadsheet:
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
ValueRange response = request.Execute();
IList<IList<Object>> values = response.Values;
if (values != null && values.Count > 0)
{
Console.WriteLine("Name, Major");
foreach (var row in values)
{
// Print columns A and E, which correspond to indices 0 and 4.
Console.WriteLine("{0}, {1}", row[0], row[4]);
}
}
else
{
Console.WriteLine("No data found.");
}
Console.Read();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
Any help to fix this problem would be much appreciated. Thanks!

How to serve non public file directly from the Amazon s3 storage

We need to work on .net based web application that will upload files to Amazon S3 Storage bucket using admin panel of the app and clients will be given to downloadable files with client.aspx file.
We looked at few example and got confused with some of the sample code for downloading non public files from S3 storage. one such example is below
AmazonS3Config config = new AmazonS3Config()
{
RegionEndpoint = RegionEndpoint.USEast1
};
IAmazonS3 client = new AmazonS3Client(accessKey, secretKey, config);
string dest = System.IO.Path.GetTempPath() + "event.mp4";
using (client)
{
GetObjectRequest request = new GetObjectRequest() { BucketName = "bucketname" + #"/" + "videos2015", Key = "event.mp4" };
using (GetObjectResponse response = client.GetObject(request))
{
response.WriteResponseStreamToFile(dest);
}
}
Response.Clear();
Response.AppendHeader("content-disposition", "attachment; filename=" + "dynamic_filename.png");
Response.ContentType = "application/octet-stream";
Response.TransmitFile(dest);
Response.Flush();
Response.End();
When user click on the link following code gets executed on web server and code downloads file on the web server and then serves the same file to client... if i am not wrong. Is there not a way that we can serve file for download directly from the AWS S3 storage.
In above case it is waste of server resources and increases the download time also.
Out files on AWS are not Public they are non public so the url is not accessible directly from client browsers as is in case of public content type
The pre-signed urls are indeed what you are looking for. Since you are using C#, here is a link to some useful code examples:
http://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLDotNetSDK.html
There is no need to upload files to s3 thru your webserver, they can be sent directly. Same thing on the download, download directly from S3 - don't copy them to EC2 first, you would be wasting bandwidth and processing resources.
You can use Minio-dotnet client library Its Open Source & supports compatible S3 API.
Here is an example for PresignedPostPolicy
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Minio;
namespace Minio.Examples
{
class PresignedPostPolicy
{
static int Main()
{
/// Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY, my-bucketname and
/// my-objectname are dummy values, please replace them with original values.
var client = new MinioClient("s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
PostPolicy form = new PostPolicy();
DateTime expiration = DateTime.UtcNow;
form.SetExpires(expiration.AddDays(10));
form.SetKey("my-objectname");
form.SetBucket("my-bucketname");
Dictionary <string, string> formData = client.PresignedPostPolicy(form);
string curlCommand = "curl ";
foreach (KeyValuePair<string, string> pair in formData)
{
curlCommand = curlCommand + " -F " + pair.Key + "=" + pair.Value;
}
curlCommand = curlCommand + " -F file=#/etc/bashrc https://s3.amazonaws.com/my-bucketname";
Console.Out.WriteLine(curlCommand);
return 0;
}
}
}
And below one for PresignedPutObject
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Minio;
namespace Minio.Examples
{
class PresignedPutObject
{
static int Main()
{
/// Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY, my-bucketname and
/// my-objectname are dummy values, please replace them with original values.
var client = new MinioClient("s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
Console.Out.WriteLine(client.PresignedPutObject("my-bucketname", "my-objectname", 1000));
return 0;
}
}
}
Hope it helps.
PS: I work for Minio

Categories