new spreadsheet not visible in google drive using .NET API - c#

I created a google spreadsheet using Google API NET like below. I am receiving no exception and from code i was able to retrieve file id also. But when I look in google drive I am not seeing file. Is there some thing I should do to notify google drive. I did a refresh my drive too. But no use. Any idea?
I authenticated my service like below:
authenticate.cs
public class SerivceAccount
{
private const string ServiceAccountEmail = "182464555438-#developer.gserviceaccount.com";
private static readonly X509Certificate2 Certificate = new X509Certificate2("ADExpress.p12", "notasecret", X509KeyStorageFlags.Exportable);
readonly ServiceAccountCredential _credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[]
{
"https://spreadsheets.google.com/feeds",
DriveService.Scope.Drive,
"https://www.googleapis.com/auth/drive.file"
}
}.FromCertificate(Certificate));
public bool Authenticate()
{
var isAuthenticated = false;
try
{
if (!_credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result)
throw new InvalidOperationException("Access token request failed.");
isAuthenticated = true;
}
catch (Exception e)
{
Console.WriteLine("Exception in Authenticating " + e.ToString());
}
return isAuthenticated;
}
public ServiceAccountCredential Credential
{
get { return _credential; }
}
}
main.cs
var account = new SerivceAccount();
if (account.Authenticate())
{
FilesResource.InsertRequest request = service.Files.Insert(new Google.Apis.Drive.v2.Data.File()
{
Title = "Test",
Description = "Test",
MimeType = "application/vnd.google-apps.spreadsheet"
});
var file = request.Execute();
Console.WriteLine("File ID: " + file.Id);
}

You are authenticating using a service account.
I did a refresh my drive too
Checking your own Google Drive web version will not show you the files that where created by a service account. Service account is its own user. Your file is probably being created on the service accounts Google Drive account you can test that by doing files.list
example:
/// <summary>
/// Retrieve a list of File resources.
/// </summary>
/// <param name="service">Drive API service instance.</param>
/// <returns>List of File resources.</returns>
public static List<File> retrieveAllFiles(DriveService service) {
List<File> result = new List<File>();
FilesResource.ListRequest request = service.Files.List();
do {
try {
FileList files = request.Execute();
result.AddRange(files.Items);
request.PageToken = files.NextPageToken;
} catch (Exception e) {
Console.WriteLine("An error occurred: " + e.Message);
request.PageToken = null;
}
} while (!String.IsNullOrEmpty(request.PageToken));
return result;
}
In order for another user to see the file you must grant them permission to see the file created by the service account.

Actually This helps to resolve my issue. I need to provide permissions to the file to be visible in UI interface. Hope this helps.
Permission newPermission = new Permission();
newPermission.Value = "yourdriveaccount#domain.com";
newPermission.Type = "user";
newPermission.Role = "reader";
service.Permissions.Insert(newPermission, file.Id).Execute();

Related

EWS Connection Giving Unauthorized (401) Error

I have been working on a program that scans an exchange inbox for specific emails from a specified address. Currently the program reads the inbox, downloads the attachment, and moves the email to another folder. However, after about 15 pulls from the EWS server, the connection starts giving a 401 Unauthorized error until I restart the program. The program is setup to login via OAuth as basic auth is disabled by the system administrator. Below is the code that I am using to obtain the exchange connection and read the emails from the inbox.
Exchange Connection Code:
public static async Task<ExchangeService> GetExchangeConnection()
{
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = AppID,
TenantId = TenantID,
};
var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();
var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };
var securePassword = new SecureString();
foreach (char c in Pasword)
securePassword.AppendChar(c);
try
{
var authResult = await pca.AcquireTokenByUsernamePassword(ewsScopes, Username, securePassword).ExecuteAsync();
ExchangeService exchangeService = new ExchangeService()
{
Credentials = new OAuthCredentials(authResult.AccessToken),
Url = new Uri("https://outlook.office365.com/ews/exchange.asmx"),
};
return exchangeService;
}
catch
{
return null;
}
}
Email Retriever
public static List<Email> RetreiveEmails()
{
ExchangeService exchangeConnection = GetExchangeConnection().Result;
try
{
List<Email> Emails = new List<Email>();
TimeSpan ts = new TimeSpan(0, -5, 0, 0);
DateTime date = DateTime.Now.Add(ts);
SearchFilter.IsGreaterThanOrEqualTo EmailTimeFilter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, date);
if (exchangeConnection != null)
{
FindItemsResults<Item> findResults = exchangeConnection.FindItems(WellKnownFolderName.Inbox, EmailTimeFilter, new ItemView(10));
foreach (Item item in findResults)
{
if (item.Subject != null)
{
EmailMessage message = EmailMessage.Bind(exchangeConnection, item.Id);
message.Load(new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.TextBody));
Emails.Add(new Email(message.DateTimeReceived, message.From.Name.ToString(), message.Subject, message.TextBody.ToString(), (message.HasAttachments) ? "Yes" : "No", message.Id.ToString()));
}
}
}
exchangeConnection = null;
return Emails;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
}
The error occurs when the email retriever tries to either create the exchange connection or when requesting the emails from the folder. In either case the code will error out and give me 401 unauthorized while using credentials that work for the first dozen times and then fails after so many attempts. I have tried it with multiple different accounts and the issue persists with all of them and I have made sure that the application is authorized to access the exchange inbox. Any suggestions or help is much appreciated.
After doing further tracing regarding the 401 error it resulted in an issue with the token reaching the end of it's 1 hour lifespan. This is due to the original OAuth token having an initial life of 1 hour. This however was able to be fixed by setting up code to automatically refresh the token when needed. Here is the code to address this issue for anyone else who comes across this problem.
Authentication Manager:
class AuthenticationManager
{
protected IPublicClientApplication App { get; set; }
public AuthenticationManager(IPublicClientApplication app)
{
App = app;
}
public async Task<AuthenticationResult> AcquireATokenFromCacheOrUsernamePasswordAsync(IEnumerable<String> scopes, string username, SecureString password)
{
AuthenticationResult result = null;
var accounts = await App.GetAccountsAsync();
if (accounts.Any())
{
try
{
result = await (App as PublicClientApplication).AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
}
catch (MsalUiRequiredException)
{ }
}
if (result == null)
{
result = await (App as PublicClientApplication).AcquireTokenByUsernamePassword(scopes, username, password).ExecuteAsync();
}
return result;
}
}
I am using direct username and password authentication but the line of code can be switched to getting the user authentication via interactive methods as well. The code essentially creates a new instance of the authentication manager with a PublicClientApplication used to initialize it which houses the appID and tenantID. After initializing, you can call the AquireATokenFromCacheOrUsernamePasswordAsync which will attempt to see if there is an account present to get a token against. Next it will attempt to retrieve the previously cached token or refresh the token if it expires in less than 5 minutes. If there is a token available it will return that to the main application. If there isn't a token available, it will acquire a new token using the username and password supplied. Implementation of this code looks something like this,
class ExchangeServices
{
AuthenticationManager Manager = null;
public ExchangeServices(String AppId, String TenantID)
{
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = AppID,
TenantId = TenantID,
};
var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();
Manager = new AuthenticationManager(pca);
}
public static async Task<ExchangeService> GetExchangeService()
{
var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" }
var securePassword = new SecureString();
foreach(char c in Password)
securePassword.AppendChar(c);
var authResult = await Manager.AquireATokenFromCacheOrUsernamePasswordAsync(ewsScopes, Username, securePassword);
ExchangeService exchangeService = new ExchangeService()
{
Credentials = new OAuthCredentials(authResult.AccessToken),
Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
};
return exchangeService;
}
}
The code above is everything laid out that is needed to create a new authentication manager and use it to get and update new tokens while using EWS services through OAuth. This is the solution that I found to fix the issue described above.

Google Drive API Files.List() Error invalid_grant Bad Request

I am using Google Apis Drive v3 with a simple request to find a folder with a specific ID and then to list all of the files in the folder.
However on request Execute it is creating the exception:
“Error: invalid_grant, Description: Bad Request, Uri:”
It used to work but has stopped sometime in the last month approx. while I was working on another project.
I have checked the API console and our Client ID is registered (I have hashed out the ID, Secret & refresh token) and everything seems OK with the creation of a service it is sending the request that is not working.
However the query used to work and I have tried changing the query and it still does not work.
Code is below, any suggestions on how to resolve this would be appreciated. Thanks
Request File List
List<File> fileList = new List<File>();
List<File> FileList_Lst_Ordered = new List<File>();
try
{
string pageToken = null;
do
{
request.Q = "'" + folderId + "' in parents";
//request.Q = String.Format("name='{0}'", "Release Scripts Folder");
//request.Q = "mimeType = 'application/vnd.google-apps.folder' + title='" + folderId +"'";
request.Spaces = "drive";
request.Fields = "nextPageToken, files(id, name, mimeType, trashed)";
request.PageToken = pageToken;
var result = request.Execute();
// Iterate through files
foreach (File file in result.Files)
{
// If it is a matching type (or we are retreiving all files)
if (!(bool)file.Trashed && (file.MimeType == type || type == "ALL"))
{
fileList.Add(file);
}
//RST_00001
else if (!(bool)file.Trashed && (file.MimeType == "sql" || file.MimeType == "text/x-sql"))
{
fileList.Add(file);
}
}
// Increment file token
pageToken = result.NextPageToken;
} while (pageToken != null);
//RST_00002: Order List Alphabetically
int Counter_int = fileList.Count();
for (int i = 1; i <= Counter_int; i++)
{
FileList_Lst_Ordered.Add(fileList.OrderBy(x => x.Name).FirstOrDefault());
fileList.Remove(fileList.OrderBy(x => x.Name).FirstOrDefault());
}
}
catch (Exception Ex)
{
errorString = Ex.Message;
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Error Occured While Obtaining script list for folder " + folderId + ", full error: " + Ex.ToString());
}
Initialise Service In GoogUtils.cs Helper
public static DriveService InitService(ref string errorString)
{
try {
var token = new TokenResponse { RefreshToken = "###" };
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "###",
ClientSecret = "###"
}
}),
"user",
token);
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = ApplicationName,
});
return service;
}
catch (Exception e)
{
errorString = e.Message;
}
return null;
}
I have been able to resolve the problem. By re-writing the service so it uses GoogleWebAuthorizationBroker.AuthorizeAsync instead of GoogleAuthorizationCodeFlow, I also am now loading the credentials from a JSON. See blow.
Thanks to all for your contributions and help:
public static DriveService InitService(ref string errorString, string Log_Filename)
{
try
{
string[] Scopes = { DriveService.Scope.DriveReadonly };
UserCredential credential;
if (!System.IO.File.Exists(credential_json))
{
System.Windows.Forms.MessageBox.Show("Unable to Initialise Google Drives Service credentials not found.");
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Google Drive Init - Unable to Initialise Google Drives Service credentials not found.");
}
using (var stream = new FileStream(credential_json, FileMode.Open, FileAccess.Read))
{
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
if (credential.UserId == "user")
{
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Google Drive Init - Credentials file saved to: " + credPath);
}
else {
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Google Drive Init - Unable to verify credentials.");
return null;
}
}
// Create Drive API service
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Validate Service
if (service.Name == "drive" && service.ApplicationName == ApplicationName)
{
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Google Drive Init - Google Service Created.");
}
else
{
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Google Drive Init - Unable to create service.");
return null;
}
return service;
}
catch (Exception Ex)
{
System.Windows.Forms.MessageBox.Show("An Error Occured While Initialising Google Drives Service");
System.Windows.Forms.Clipboard.SetText(Ex.ToString());
Helpers.Helpers_TextFile.TextFile_WriteTo(Log_Filename, "Google Drive Init - Error Occurred: " + Ex.ToString());
errorString = Ex.Message;
}
return null;
}

Access Google Calendar API using Service Account Authentication

I was able to access the Google Calendar API using the .NET Quickstart tutorial and it worked great!
The problem with that tutorial is that it uses Open Authentication or OAuth2. I will like to do the same using Service Account Authentication.
(https://support.google.com/googleapi/answer/6158857?hl=en)
Can someone give me an example of how I can access my calendar using a Service account key file?
I have tried also tried using Google Calendar API Authentication with C# tutorial and was not able to accomplish it.
I am curious as to why your first attempt with the service account tutorial didn't work. What was wrong? Was there an error?
Remember service accounts are not you. The service account has its own Google calendar account, so if you are trying to read one of your "personal calendars", it will not work. You are going to have to share your personal calendar with the service account.
Here is another example using the Json service account key file.
string[] scopes = new string[] { CalendarService.Scope.Calendar };
GoogleCredential credential;
using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(scopes);
}
// Create the Calendar service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar Authentication Sample",
});
Try this. First Create a service account which can be found in your Google Dev Console.
For reference on implementation, check DalmTo's blogpost on how to use Service Accounts here.
Here's a snippet:
var certificate = new X509Certificate2(keyFile, "notasecret", X509KeyStorageFlags.Exportable);
try{
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
//Create the service.
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample"
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
For anybody finding their way to this question but needing a NodeJS solution, this is how you can log in with a service account that has domain-wide delegation permissions as a specific user:
const auth = new google.auth.JWT({ // use JWT instead of GoogleAuth
subject: "me#mycompany.com", // specify subject (user whose context you want to operate in)
keyFile: "service-account-key.json",
scopes: [
"https://www.googleapis.com/auth/calendar.events",
"https://www.googleapis.com/auth/calendar.readonly"
],
})
visit this link have complete working project google service account authentication with google calendar event insert method you have to change your json private key and your Credentials only
https://github.com/CodeForget/Google-Service-Account-Authentication
here json key file othentication as well p12 authentication both
ServiceAccountAuthentication.cs
using Google.Apis.Calendar.v3;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Calendar.v3.Data;
namespace GoogleSamplecSharpSample.Calendarv3.Auth
{
public static class ServiceAccountExample
{
/// <summary>
/// Authenticating to Google calender using a Service account
/// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
/// </summary>
/// Both param pass from webform1.aspx page on page load
/// <param name="serviceAccountEmail">From Google Developer console https://console.developers.google.com/projectselector/iam-admin/serviceaccounts </param>
/// <param name="serviceAccountCredentialFilePath">Location of the .p12 or Json Service account key file downloaded from Google Developer console https://console.developers.google.com/projectselector/iam-admin/serviceaccounts </param>
/// <returns>AnalyticsService used to make requests against the Analytics API</returns>
public static CalendarService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath, string[] scopes)
{
try
{
if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
throw new Exception("Path to the service account credentials file is required.");
if (!File.Exists(serviceAccountCredentialFilePath))
throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
if (string.IsNullOrEmpty(serviceAccountEmail))
throw new Exception("ServiceAccountEmail is required.");
// For Json file
if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
{
GoogleCredential credential;
//using(FileStream stream = File.Open(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read, FileShare.None))
using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(scopes).CreateWithUser("xyz#gmail.com");//put a email address from which you want to send calendar its like (calendar by xyz user )
}
// Create the Calendar service.
return new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar_Appointment event Using Service Account Authentication",
});
}
else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
{ // If its a P12 file
var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
// Create the Calendar service.
return new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar_Appointment event Using Service Account Authentication",
});
}
else
{
throw new Exception("Something Wrong With Service accounts credentials.");
}
}
catch (Exception ex)
{
throw new Exception("Create_Service_Account_Calendar_Failed", ex);
}
}
}
}
add webform.aspx and put this code on webform.aspx.cs
using System;
using Google.Apis.Calendar.v3;
using GoogleSamplecSharpSample.Calendarv3.Auth;
using Google.Apis.Calendar.v3.Data;
namespace CalendarServerToServerApi
{
public partial class WebForm1 : System.Web.UI.Page
{
// create event which you want to set using service account authentication
Event myEvent = new Event
{
Summary = "Visa Counselling",
Location = "Gurgaon sector 57",
Start = new EventDateTime()
{
DateTime = new DateTime(2017, 10, 4, 2, 0, 0),
TimeZone = "(GMT+05:30) India Standard Time"
},
End = new EventDateTime()
{
DateTime = new DateTime(2017, 10, 4, 2, 30, 0),
TimeZone = "(GMT+05:30) India Standard Time"
}
//,
// Recurrence = new String[] {
//"RRULE:FREQ=WEEKLY;BYDAY=MO"
//}
//,
// Attendees = new List<EventAttendee>()
// {
// new EventAttendee() { Email = "Srivastava998#gmail.com" }
//}
};
protected void Page_Load(object sender, EventArgs e)
{
}
public void Authenticate(object o, EventArgs e)
{
string[] scopes = new string[] {
CalendarService.Scope.Calendar //, // Manage your calendars
//CalendarService.Scope.CalendarReadonly // View your Calendars
};
string cal_user = "calenderID#gamil.com"; //your CalendarID On which you want to put events
//you get your calender id "https://calendar.google.com/calendar"
//go to setting >>calenders tab >> select calendar >>Under calender Detailes at Calendar Address:
string filePath = Server.MapPath("~/Key/key.json");
var service = ServiceAccountExample.AuthenticateServiceAccount("xyz#projectName.iam.gserviceaccount.com", filePath, scopes);
//"xyz#projectName.iam.gserviceaccount.com" this is your service account email id replace with your service account emailID you got it .
//when you create service account https://console.developers.google.com/projectselector/iam-admin/serviceaccounts
insert(service, cal_user, myEvent);
}
public static Event insert(CalendarService service, string id, Event myEvent)
{
try
{
return service.Events.Insert(myEvent, id).Execute();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
}

Create folder if it does not exist in the Google Drive in Asp.net

I am creating an app in Asp.net MVC in which I want to upload a file on google drive. The following code successfully creating a new folder and saving the file in that folder:
public void Upload(string fileName, byte[] bytes)
{
if (_userCredential != null)
{
var service = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = _userCredential,
ApplicationName = "Test App"
});
var file = new File { Title = "Test folder", MimeType = "application/vnd.google-apps.folder" };
var result = service.Files.Insert(file).Execute();
var saveresult = result.Id;
Google.Apis.Drive.v2.Data.File body = new Google.Apis.Drive.v2.Data.File();
body.Title = fileName;
body.Description = "A test document";
body.MimeType = "application/zip";
body.Parents = new List<ParentReference>() { new ParentReference() { Id = saveresult } };
var stream = new System.IO.MemoryStream(bytes);
FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, "text/plain");
request.Upload();
if (request.ResponseBody == null)
{
throw new Exception("User remove access to aplication.");
}
}
}
Now the problem is after saving a file once when I am trying to save the file again it creates another folder and save the file in that folder but I want to check that if folder exists than it save the file in that folder and if the folder doesn't exist than it first creates a folder than save that file in that.
Thanks.
You can check for more details.
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
// ...
public class MyClass {
// ...
/// <summary>
/// Print files belonging to a folder.
/// </summary>
/// <param name="service">Drive API service instance.</param>
/// <param name="folderId">ID of the folder to print files from</param>
public static void PrintFilesInFolder(DriveService service,
String folderId) {
ChildrenResource.ListRequest request = service.Children.List(folderId);
do {
try {
ChildList children = request.Execute();
foreach (ChildReference child in children.Items) {
Console.WriteLine("File Id: " + child.Id);
}
request.PageToken = children.NextPageToken;
} catch (Exception e) {
Console.WriteLine("An error occurred: " + e.Message);
request.PageToken = null;
}
} while (!String.IsNullOrEmpty(request.PageToken));
}
// ...
}

Uploading objects to google cloud storage buckets in c#

Can someone please provide an example of how to use Google.Apis.Storage.v1 for uploading files to google cloud storage in c#?
I found that this basic operation is not as straight forward as you might expect. Google's documentation about it's Storage API is lacking in information about using it in C# (or any other .NET language). Searching for 'how to upload file to google cloud storage in c#' didn't exactly help me, so here is my working solution with some comments:
Preparation:
You need to create OAuth2 account in your Google Developers Console - go to Project/APIs & auth/Credentials.
Copy Client ID & Client Secret to your code. You will also need your Project name.
Code (it assumes that you've added Google.Apis.Storage.v1 via NuGet):
First, you need to authorize your requests:
var clientSecrets = new ClientSecrets();
clientSecrets.ClientId = clientId;
clientSecrets.ClientSecret = clientSecret;
//there are different scopes, which you can find here https://cloud.google.com/storage/docs/authentication
var scopes = new[] {#"https://www.googleapis.com/auth/devstorage.full_control"};
var cts = new CancellationTokenSource();
var userCredential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets,scopes, "yourGoogle#email", cts.Token);
Sometimes you might also want to refresh authorization token via:
await userCredential.RefreshTokenAsync(cts.Token);
You also need to create Storage Service:
var service = new Google.Apis.Storage.v1.StorageService();
Now you can make requests to Google Storage API.
Let's start with creating a new bucket:
var newBucket = new Google.Apis.Storage.v1.Data.Bucket()
{
Name = "your-bucket-name-1"
};
var newBucketQuery = service.Buckets.Insert(newBucket, projectName);
newBucketQuery.OauthToken = userCredential.Result.Token.AccessToken;
//you probably want to wrap this into try..catch block
newBucketQuery.Execute();
And it's done. Now, you can send a request to get list of all of your buckets:
var bucketsQuery = service.Buckets.List(projectName);
bucketsQuery.OauthToken = userCredential.Result.Token.AccessToken;
var buckets = bucketsQuery.Execute();
Last part is uploading new file:
//enter bucket name to which you want to upload file
var bucketToUpload = buckets.Items.FirstOrDefault().Name;
var newObject = new Object()
{
Bucket = bucketToUpload,
Name = "some-file-"+new Random().Next(1,666)
};
FileStream fileStream = null;
try
{
var dir = Directory.GetCurrentDirectory();
var path = Path.Combine(dir, "test.png");
fileStream = new FileStream(path, FileMode.Open);
var uploadRequest = new Google.Apis.Storage.v1.ObjectsResource.InsertMediaUpload(service, newObject,
bucketToUpload,fileStream,"image/png");
uploadRequest.OauthToken = userCredential.Result.Token.AccessToken;
await uploadRequest.UploadAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (fileStream != null)
{
fileStream.Dispose();
}
}
And bam! New file will be visible in you Google Developers Console inside of selected bucket.
You can use Google Cloud APIs without SDK in the following ways:
Required api-key.json file
Install package Google.Apis.Auth.OAuth2 in order to authorize the
HTTP web request
You can set the default configuration for your application in this
way
I did the same using .NET core web API and details are given below:
Url details:
"GoogleCloudStorageBaseUrl": "https://www.googleapis.com/upload/storage/v1/b/",
"GoogleSpeechBaseUrl": "https://speech.googleapis.com/v1/operations/",
"GoogleLongRunningRecognizeBaseUrl": "https://speech.googleapis.com/v1/speech:longrunningrecognize",
"GoogleCloudScope": "https://www.googleapis.com/auth/cloud-platform",
public void GetConfiguration()
{
// Set global configuration
bucketName = _configuration.GetValue<string>("BucketName");
googleCloudStorageBaseUrl = _configuration.GetValue<string>("GoogleCloudStorageBaseUrl");
googleSpeechBaseUrl = _configuration.GetValue<string>("GoogleSpeechBaseUrl");
googleLongRunningRecognizeBaseUrl = _configuration.GetValue<string>("GoogleLongRunningRecognizeBaseUrl");
// Set google cloud credentials
string googleApplicationCredentialsPath = _configuration.GetValue<string>("GoogleCloudCredentialPath");
using (Stream stream = new FileStream(googleApplicationCredentialsPath, FileMode.Open, FileAccess.Read))
googleCredential = GoogleCredential.FromStream(stream).CreateScoped(_configuration.GetValue<string>("GoogleCloudScope"));
}
Get Oauth token:
public string GetOAuthToken()
{
return googleCredential.UnderlyingCredential.GetAccessTokenForRequestAsync("https://accounts.google.com/o/oauth2/v2/auth", CancellationToken.None).Result;
}
To upload file to cloud bucket:
public async Task<string> UploadMediaToCloud(string filePath, string objectName = null)
{
string bearerToken = GetOAuthToken();
byte[] fileBytes = File.ReadAllBytes(filePath);
objectName = objectName ?? Path.GetFileName(filePath);
var baseUrl = new Uri(string.Format(googleCloudStorageBaseUrl + "" + bucketName + "/o?uploadType=media&name=" + objectName + ""));
using (WebClient client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + bearerToken);
client.Headers.Add(HttpRequestHeader.ContentType, "application/octet-stream");
byte[] response = await Task.Run(() => client.UploadData(baseUrl, "POST", fileBytes));
string responseInString = Encoding.UTF8.GetString(response);
return responseInString;
}
}
In order to perform any action to the cloud API, just need to make a HttpClient get/post request as per the requirement.
Thanks
This is for Google.Cloud.Storage.V1 (not Google.Apis.Storage.v1), but appears to be a bit simpler to perform an upload now. I started with the Client libraries "Getting Started" instructions to create a service account and bucket, then experimented to find out how to upload an image.
The process I followed was:
Sign up for Google Cloud free trial
Create a new project in Google Cloud (remember the project name\ID for later)
Create a Project Owner service account - this will result in a json file being downloaded that contains the service account credentials. Remember where you put that file.
The getting started docs get you to add the path to the JSON credentials file into an environment variable called GOOGLE_APPLICATION_CREDENTIALS - I couldn't get this to work through the provided instructions. Turns out it is not required, as you can just read the JSON file into a string and pass it to the client constructor.
I created an empty WPF project as a starting point, and a single ViewModel to house the application logic.
Install the Google.Cloud.Storage.V1 nuget package and it should pull in all the dependencies it needs.
Onto the code.
MainWindow.xaml
<StackPanel>
<Button
Margin="50"
Height="50"
Content="BEGIN UPLOAD"
Click="OnButtonClick" />
<ContentControl
Content="{Binding Path=ProgressBar}" />
</StackPanel>
MainWindow.xaml.cs
public partial class MainWindow
{
readonly ViewModel _viewModel;
public MainWindow()
{
_viewModel = new ViewModel(Dispatcher);
DataContext = _viewModel;
InitializeComponent();
}
void OnButtonClick(object sender, RoutedEventArgs args)
{
_viewModel.UploadAsync().ConfigureAwait(false);
}
}
ViewModel.cs
public class ViewModel
{
readonly Dispatcher _dispatcher;
public ViewModel(Dispatcher dispatcher)
{
_dispatcher = dispatcher;
ProgressBar = new ProgressBar {Height=30};
}
public async Task UploadAsync()
{
// Google Cloud Platform project ID.
const string projectId = "project-id-goes-here";
// The name for the new bucket.
const string bucketName = projectId + "-test-bucket";
// Path to the file to upload
const string filePath = #"C:\path\to\image.jpg";
var newObject = new Google.Apis.Storage.v1.Data.Object
{
Bucket = bucketName,
Name = System.IO.Path.GetFileNameWithoutExtension(filePath),
ContentType = "image/jpeg"
};
// read the JSON credential file saved when you created the service account
var credential = Google.Apis.Auth.OAuth2.GoogleCredential.FromJson(System.IO.File.ReadAllText(
#"c:\path\to\service-account-credentials.json"));
// Instantiates a client.
using (var storageClient = Google.Cloud.Storage.V1.StorageClient.Create(credential))
{
try
{
// Creates the new bucket. Only required the first time.
// You can also create buckets through the GCP cloud console web interface
storageClient.CreateBucket(projectId, bucketName);
System.Windows.MessageBox.Show($"Bucket {bucketName} created.");
// Open the image file filestream
using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Open))
{
ProgressBar.Maximum = fileStream.Length;
// set minimum chunksize just to see progress updating
var uploadObjectOptions = new Google.Cloud.Storage.V1.UploadObjectOptions
{
ChunkSize = Google.Cloud.Storage.V1.UploadObjectOptions.MinimumChunkSize
};
// Hook up the progress callback
var progressReporter = new Progress<Google.Apis.Upload.IUploadProgress>(OnUploadProgress);
await storageClient.UploadObjectAsync(
newObject,
fileStream,
uploadObjectOptions,
progress: progressReporter)
.ConfigureAwait(false);
}
}
catch (Google.GoogleApiException e)
when (e.Error.Code == 409)
{
// When creating the bucket - The bucket already exists. That's fine.
System.Windows.MessageBox.Show(e.Error.Message);
}
catch (Exception e)
{
// other exception
System.Windows.MessageBox.Show(e.Message);
}
}
}
// Called when progress updates
void OnUploadProgress(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case Google.Apis.Upload.UploadStatus.Starting:
ProgressBar.Minimum = 0;
ProgressBar.Value = 0;
break;
case Google.Apis.Upload.UploadStatus.Completed:
ProgressBar.Value = ProgressBar.Maximum;
System.Windows.MessageBox.Show("Upload completed");
break;
case Google.Apis.Upload.UploadStatus.Uploading:
UpdateProgressBar(progress.BytesSent);
break;
case Google.Apis.Upload.UploadStatus.Failed:
System.Windows.MessageBox.Show("Upload failed"
+ Environment.NewLine
+ progress.Exception);
break;
}
}
void UpdateProgressBar(long value)
{
_dispatcher.Invoke(() => { ProgressBar.Value = value; });
}
// probably better to expose progress value directly and bind to
// a ProgressBar in the XAML
public ProgressBar ProgressBar { get; }
}
Use of Google.Apis.Storage.v1 for uploading files using SDK to google cloud storage in c#:
Required api-key.json file
Install the package Google.Cloud.Storage.V1; and Google.Apis.Auth.OAuth2;
The code is given below to upload the file to the cloud
private string UploadFile(string localPath, string objectName = null)
{
string projectId = ((Google.Apis.Auth.OAuth2.ServiceAccountCredential)googleCredential.UnderlyingCredential).ProjectId;
try
{
// Creates the new bucket.
var objResult = storageClient.CreateBucket(projectId, bucketName);
if (!string.IsNullOrEmpty(objResult.Id))
{
// Upload file to google cloud server
using (var f = File.OpenRead(localPath))
{
objectName = objectName ?? Path.GetFileName(localPath);
var objFileUploadStatus1 = storageClient.UploadObject(bucketName, objectName, null, f);
}
}
}
catch (Google.GoogleApiException ex)
{
// Error code =409, means bucket already created/exist then upload file in the bucket
if (ex.Error.Code == 409)
{
// Upload file to google cloud server
using (var f = File.OpenRead(localPath))
{
objectName = objectName ?? Path.GetFileName(localPath);
var objFileUploadStatus2 = storageClient.UploadObject(bucketName, objectName, null, f);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
return objectName;
}
To set the credentials
private bool SetStorageCredentials()
{
bool status = true;
try
{
if (File.Exists(credential_path))
{
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", credential_path);
using (Stream objStream = new FileStream(Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS"), FileMode.Open, FileAccess.Read))
googleCredential = GoogleCredential.FromStream(objStream);
// Instantiates a client.
storageClient = StorageClient.Create();
channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host, googleCredential.ToChannelCredentials());
}
else
{
DialogResult result = MessageBox.Show("File " + Path.GetFileName(credential_path) + " does not exist. Please provide the correct path.");
if (result == System.Windows.Forms.DialogResult.OK)
{
status = false;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
status = false;
}
return status;
}
I used SDK in one of my window application. You can use the same code according to your needs/requirements.
You'll be happy to know it still works in 2016...
I was googling all over using fancy key words like "google gcp C# upload image", until I just plain asked the question: "How do I upload an image to google bucket using C#"... and here I am. I removed the .Result in the user credential, and this was the final edit that worked for me.
// ******
static string bucketForImage = ConfigurationManager.AppSettings["testStorageName"];
static string projectName = ConfigurationManager.AppSettings["GCPProjectName"];
string gcpPath = Path.Combine(Server.MapPath("~/Images/Gallery/"), uniqueGcpName + ext);
var clientSecrets = new ClientSecrets();
clientSecrets.ClientId = ConfigurationManager.AppSettings["GCPClientID"];
clientSecrets.ClientSecret = ConfigurationManager.AppSettings["GCPClientSc"];
var scopes = new[] { #"https://www.googleapis.com/auth/devstorage.full_control" };
var cts = new CancellationTokenSource();
var userCredential = await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, scopes, ConfigurationManager.AppSettings["GCPAccountEmail"], cts.Token);
var service = new Google.Apis.Storage.v1.StorageService();
var bucketToUpload = bucketForImage;
var newObject = new Google.Apis.Storage.v1.Data.Object()
{
Bucket = bucketToUpload,
Name = bkFileName
};
FileStream fileStream = null;
try
{
fileStream = new FileStream(gcpPath, FileMode.Open);
var uploadRequest = new Google.Apis.Storage.v1.ObjectsResource.InsertMediaUpload(service, newObject,
bucketToUpload, fileStream, "image/"+ ext);
uploadRequest.OauthToken = userCredential.Token.AccessToken;
await uploadRequest.UploadAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (fileStream != null)
{
fileStream.Dispose();
}
}
// ******
Here is the link to their official C# example of ".NET Bookshelf App" using Google Cloud storage.
https://cloud.google.com/dotnet/docs/getting-started/using-cloud-storage
Source on github:
https://github.com/GoogleCloudPlatform/getting-started-dotnet/blob/master/aspnet/3-binary-data/Services/ImageUploader.cs
https://github.com/GoogleCloudPlatform/getting-started-dotnet/tree/master/aspnet/3-binary-data
Nuget
https://www.nuget.org/packages/Google.Cloud.Storage.V1/
Here are 2 examples that helped me to upload files to a bucket in Google Cloud Storage with Google.Cloud.Storage.V1 (not Google.Apis.Storage.v1):
Upload files to Google cloud storage using c#
Uploading .csv Files to Google Cloud Storage using C# .Net
I got both working on a C# Console Application just for testing purposes.
#February 2021
string _projectId = "YOUR-PROJECT-ID-GCP"; //ProjectID also present in the json file
GoogleCredential _credential = GoogleCredential.FromFile("credential-cloud-file-123418c9e06c.json");
/// <summary>
/// UploadFile to GCS Bucket
/// </summary>
/// <param name="bucketName"></param>
/// <param name="localPath">my-local-path/my-file-name</param>
/// <param name="objectName">my-file-name</param>
public void UploadFile(string bucketName, string localPath, string objectName)
{
var storage = StorageClient.Create(_credential);
using var fileStream = File.OpenRead(localPath);
storage.UploadObject(bucketName, objectName, null, fileStream);
Console.WriteLine($"Uploaded {objectName}.");
}
You get the credentials JSON file from the google cloud portal where you create a bucket under your project..
Simple, with auth:
private void SaveFileToGoogleStorage(string path, string? fileName, string ext)
{
var filePath = Path.Combine(path, fileName + ext);
var gcCredentialsPath = Path.Combine(Environment.CurrentDirectory, "gc_sa_key.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", gcCredentialsPath);
var gcsStorage = StorageClient.Create();
using var f = File.OpenRead(filePath);
var objectName = Path.GetFileName(filePath);
gcsStorage.UploadObject(_bucketName, objectName, null, f);
Console.WriteLine($"Uploaded {objectName}.");
}

Categories