I've been scouring the internet for a few hours trying to figure out what is necessary to upload a file that will be contained within a Team Drive.
I've read most of the documentation, the only interesting bits / mention of team drives I found are here, but unfortunately there's no specifics:
https://developers.google.com/drive/v3/web/manage-uploads
https://developers.google.com/drive/v3/web/manage-teamdrives
https://developers.google.com/drive/v3/web/about-files
I'm using the .Net gapi nuget package (v3). Create a service like the following:
string[] scopes = new string[] { DriveService.Scope.Drive, DriveService.Scope.DriveFile };
var secrets = new ClientSecrets
{
ClientId = "...",
ClientSecret = "...",
};
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scopes, Environment.UserName, CancellationToken.None).Result;
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "...",
});
I have the Id of the Team Drive I'm targetting, and I can successfully retrieve the TeamDrive by the following code, but there are no interesting methods here for uploading:
var teamDrive = service.Teamdrives.Get(driveFolderId).Execute();
I've currently been trying to use the normal CreateMediaUpload way of creating a file.
File body = new File();
body.Name = name;
body.MimeType = "application/octet-stream";
FilesResource.CreateMediaUpload request = service.Files.Create(body, stream, "text/plain");
request.Upload();
There's a few interesting properties on File, namely Parents and also TeamDriveId. When setting the TeamDriveId to the Team Drive Id, the file ends up in my personal drive in the root directory. When setting the parent to the Team Drive Id, I can't seem to find the file anywhere.
There are no errors thrown, and the result of request.Upload() indicates Success/Complete every time (even if the file doesn't show up). Where else should I be looking to set the parent team drive? There's no other interesting properties on File, DriveService, or TeamDrive so I'm pretty lost.
In addition to setting to the parent to the team drive id, you must also set the SupportsTeamDrives property to true in the request.
The code would then look similar to the following (I've noted the important lines):
File body = new File();
body.Name = name;
body.MimeType = "application/octet-stream";
body.Parents = new List<string> { driveFolderId }; // <--------
FilesResource.CreateMediaUpload request = service.Files.Create(body, stream, "application/octet-stream");
request.SupportsTeamDrives = true; // <--------
request.Upload();
The key here is that the Team Drives permission scheme is completely different to the personal drive permission scheme, so you need to explicitly opt-in to it to prove you understand the differences.
An extra bit of info, if you want to list or search for files in a team drive, you must also specify IncludeTeamDriveItems and Corpora on the request (in addition to SupportsTeamDrives).
A Search might then look like this
var existingSearch = service.Files.List();
existingSearch.Fields = "nextPageToken, files(id, name)";
existingSearch.Q = $"'{driveFolderId}' in parents and name = '{name}'";
if (isFolderTeamDrive)
{
existingSearch.SupportsTeamDrives = true;
existingSearch.Corpora = "teamDrive";
existingSearch.IncludeTeamDriveItems = true;
existingSearch.TeamDriveId = driveFolderId;
}
var existingResponse = existingSearch.Execute();
Related
I have a C# desktop app which allows the user to backup its to google drive via the google drive api V3.
I have the following method in a class which is used to load the backups
static string[] Scopes = { DriveService.Scope.DriveFile };
static string ApplicationName = "MyApp";
private static string CreateFile(string pFilePath, string parentFolderId, DriveService service)
{
var fileMetaData = new Google.Apis.Drive.v3.Data.File();
fileMetaData.Name = Path.GetFileName(pFilePath);
fileMetaData.MimeType = "application/vnd.google-apps.file";
fileMetaData.Parents = new List<string> { parentFolderId };
FilesResource.CreateMediaUpload request;
using(var stream = new FileStream(pFilePath, FileMode.Open))
{
request = service.Files.Create(fileMetaData, stream, "application/vnd.google-apps.file");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
return file.Id;
}
Upon reaching request.Upload(), the request doesn't seem to have any issues, but later on after the file declaration, file turns out to be null, so no response body and thus no id either.
Is there something wrong with my request? I tried to see if I could catch an Exception in upload, which the method description claims would be of type IUploadProgress.Exception, but such an interface doesn't actually have an Exception property.
I've already authenticated and I managed to create a folder (which is the ID passed by argument parentFolderId, confirmed to not be null), so I am able to communicate with drive, just that this particular upload isn't working.
This is the code i use How to upload a file to Google Drive with C# .net I use the async method personally
string uploadedFileId;
// Create a new file on Google Drive
await using (var fsSource = new FileStream(UploadFileName, FileMode.Open, FileAccess.Read))
{
// Create a new file, with metadata and stream.
var request = service.Files.Create(fileMetadata, fsSource, "text/plain");
request.Fields = "*";
var results = await request.UploadAsync(CancellationToken.None);
if (results.Status == UploadStatus.Failed)
{
Console.WriteLine($"Error uploading file: {results.Exception.Message}");
}
// the file id of the new file we created
uploadedFileId = request.ResponseBody?.Id;
}
As for your code i think you need execute on the end
request.Upload().Execute;
I am trying to create an external shortcut to some web on Google shared drive. There is the documentation: https://developers.google.com/drive/api/v3/third-party-shortcuts but I have no idea how to pass URL to the page I want.
var credential = GoogleCredential.FromJson(jsonSecret)
.CreateScoped(Scopes)
.CreateWithUser("myemail#company.com")
.UnderlyingCredential as ServiceAccountCredential;
DriveService _driveService = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "to-teamdrive"
});
var fileMetadata = new File()
{
Name = "Test shortcut",
MimeType = "application/vnd.google-apps.drive-sdk",
Parents = new List<string> { "0AKSia6LzF-hvuk9PVA"}
};
var uploadRequest = _driveService.Files.Create(fileMetadata);
uploadRequest.SupportsAllDrives = true;
uploadRequest.Fields = "id";
var file = uploadRequest.Execute();
Additionally, executing this code throws:
Message="The application associated with this shortcut file does not support shared drives."
I can create a "link" in my drive when skipping Parents property but even then I do not know how to add URL to this "link". It creates empty... file... on my drive.
Looks like those external links were not something I was looking for. To solve my problem it is enough to create a file with name: 'name.url', mimeType 'text/x-url' and content similar to this:
[InternetShortcut]
URL=<URL here>
I have no idea if this is common or works only for my organization (we have some modifications done to the google apps).
I'm trying to download captions from some videos on Youtube using their nuget package. Here's some code:
var request = _youtube.Search.List("snippet,id");
request.Q = "Bill Gates";
request.MaxResults = 50;
request.Type = "video";
var results = request.Execute();
foreach (var result in results.Items)
{
var captionListRequest = _youtube.Captions.List("id,snippet", result.Id.VideoId);
var captionListResponse = captionListRequest.Execute();
var russianCaptions =
captionListResponse.Items.FirstOrDefault(c => c.Snippet.Language.ToLower() == "ru");
if (russianCaptions != null)
{
var downloadRequest = _youtube.Captions.Download(russianCaptions.Id);
downloadRequest.Tfmt = CaptionsResource.DownloadRequest.TfmtEnum.Srt;
var ms = new MemoryStream();
downloadRequest.Download(ms);
}
}
When the Download method is called I'm getting a weird Newtonsoft.JSON Exception that says:
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: T. Path '', line 0, position 0.'
at Newtonsoft.Json.JsonTextReader.ParseValue()
I've read some other threads on captions downloading problems and have tried to change my authorization workflow: first I've tried to use just the ApiKey but then also tried OAuth. Here's how it looks now:
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "CLIENT_ID",
ClientSecret = "CLIENT_SECRET"
},
new[] { YouTubeService.Scope.YoutubeForceSsl },
"user",
CancellationToken.None,
new FileDataStore("Youtube.CaptionsCrawler")).Result;
_youtube = new YouTubeService(new BaseClientService.Initializer
{
ApplicationName = "LKS Captions downloader",
HttpClientInitializer = credential
});
So, is it even possible to do what I'm trying to achieve?
P.S. I was able to dig deep into the youtube nuget package and as I see, the actual message, that I get (that Newtonsoft.JSON is trying to deserialize, huh!) is "The permissions associated with the request are not sufficient to download the caption track. The request might not be properly authorized, or the video order might not have enabled third-party contributions for this caption."
So, do I have to be the video owner to download captions? But if so, how do other programs like Google2SRT work?
Found this post How to get "transcript" in youtube-api v3
You can get them via GET request on: http://video.google.com/timedtext?lang={LANG}&v={VIDEOID}
Example:
http://video.google.com/timedtext?lang=en&v=-osCkzoL53U
Note that they should have subtitles added, will not work if auto-generated.
I'm trying to get Folders and Files from Google Drive, and there is some non-obvious stuff I can't understand.
First of all, I'm getting 37 items as a result - and that's wrong, because I never had 37 files on my Drive, so where all those files came from ?
Second, I'm not receiving file metadata like "Size" or "Extension" or "Parents" - all these properties are "null" for at least half of all items returned - can someone explain what means is "Parents" are "null"?
And the last thing, can I get files and folders step-by-step, like in OneDrive, e.g., get all files and folders from root of a drive, and later get children from selected folder (or download item if it's file)?
As far as I know, I have to get all files and folders and then build a tree to show it to user, as for me that's not good, because of multi-parenting stuff etc.
Here's code I use to get files and properties:
public UserCredential Authorize()
{
UserCredential credential = null;
using (var stream =
new FileStream("path_to_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, "path_to_save_creds.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
}
return credential;
}
public List<File> RetrieveAllFiles(DriveService service)
{
List<File> result = new List<File>();
FilesResource.ListRequest request = service.Files.List();
request.Fields =
"nextPageToken, files(name, id, size, kind, parents, sharedWithMeTime, shared, sharingUser, fileExtension, viewedByMe, viewedByMeTime, trashed)";
request.Spaces = "drive";
do
{
try
{
FileList files = request.Execute();
result.AddRange(files.Files);
request.PageToken = files.NextPageToken;
}
catch (Exception e)
{
/*handle exception here*/
}
} while (!String.IsNullOrEmpty(request.PageToken));
return result;
}
Here's how do I call methods described above:
UserCredential credential = Authorize();
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var googleFiles = RetrieveAllFiles(service);
At this point, I have 37 items, and such properties as "fileExtension", "viewedByMe", "parents" are null. Can someone explain what happens?
First, you might want to check how your authorization to see who's account is being accessed.
Second, try checking the OAuth 2.0 Playground as suggested in the related SO post to test whether something wrong in your code or in the API. Also stated in the post that files like folders, Google Docs which are simply links will be null.
For the Tree Command like function for your Google Drive, it will all depend on how your approach is. I found a tutorial using Apps Script which will show the basic steps on getting a hierarchical tree diagram for your Drive files.
Hope this helps.
I can setup a Google user content connection with Google API and can get folders and even upload/create files to folders. But the same connection used to create a new regular folder anywhere gives the error invalid_grant with no further information. I am passing in a valid folderId to set as parent. I have tried different scopes and a long shot of syncing my clock like I have seen on other solutions. I can't seem to figure out the issue. Anyone else have seen this?
Here is code that works to upload a file to a folder:
public async Task<string> Upload(string localFilePath, string folderId, string fileName, string refreshToken, string userId)
{
var file = new File();
file.MimeType = GetMimeType(localFilePath);
file.Name = fileName;
file.Parents = new List<string> { folderId };
var driveService = await GetDriveService(refreshToken, userId);
// File's content.
var byteArray = System.IO.File.ReadAllBytes(localFilePath);
var stream = new System.IO.MemoryStream(byteArray);
var request = driveService.Files.Create(file, stream, file.MimeType);
await request.UploadAsync();
return request.ResponseBody.Id;
}
Here is what I've been trying in creating a folder:
public async Task<string> CreateFolder(string folderId, string folderName, string refreshToken, string userId)
{
var newFolderId = string.Empty;
var driveService = await GetDriveService(refreshToken, userId);
FilesResource.ListRequest list = driveService.Files.List();
var file = new File();
file.MimeType = "application/vnd.google-apps.folder";
file.Name = folderName;
file.Parents = new List<string> { folderId };
var request = driveService.Files.Create(file);
request.Fields = "Id";
var newFolder = await request.ExecuteAsync();
return newFolder.Id;
}
And, here is the method in defining driveService:
private async Task<DriveService> GetDriveService(string refreshToken, string userId)
{
var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets: new ClientSecrets { ClientId = "xyz.apps.googleusercontent.com", ClientSecret = "xyz" },
scopes: new string[] { DriveService.Scope.Drive, DriveService.Scope.DriveFile },
dataStore: new MemoryDataStore(userId, refreshToken),
user: userId,
taskCancellationToken: CancellationToken.None);
var driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "XYZ"
});
return driveService;
}
As mentioned in creating a folder,
In the Drive API, a folder is essentially a file — one identified by the special folder MIME type application/vnd.google-apps.folder. You can create a new folder by inserting a file with this MIME type and a folder title. Do not include an extension when setting a folder title.
Please try using the given code:
File fileMetadata = new File();
fileMetadata.setName("Invoices");
fileMetadata.setMimeType("application/vnd.google-apps.folder");
File file = driveService.files().create(fileMetadata)
.setFields("id")
.execute();
System.out.println("Folder ID: " + file.getId());
I apologize, I am a victim of a copy/paste or fat finger error that I just did not notice unfortunately until now. I am managing multiple cloud connections in this app for similar purposes, and at the beginning of the call in this case to create folder I either copy/pasted or fat fingered an enum value that was improperly requesting to get the saved refreshtoken from a different cloud provider ala one drive or dropbox. Once switched to pull the saved google refreshtoken, it works. Well, at least there is a working example of create folder and upload file above :-) Btw, the line request.Fields = "Id"; needs to be request.Fields = "id"; where id needs to be lower case...otherwise a different error pops up saying the field is invalid. Thanks.