Upload a file from android to Google drive is failing. Seems like the createFile is not working even the status returned is success. The actual status error code and message that I get from the Google API is error code 8, error message: Provided DriveId is not valid
I also cannot find the file on the related user Google drive (using web interface for the Google drive).
I have also tried using Drive.DriveApi.fetchDriveId(m_api, m_file.getDriveId().toString()) in the create file callback (instead of calling to openContents), and it has also failed.
Related code is below, any help would be appreciated!
public void createFile() {
Drive.DriveApi.newContents(m_api).setResultCallback(contentsCallback);
}
final private ResultCallback<ContentsResult> contentsCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (handleError(result.getStatus())) {
return;
}
Contents contents = result.getContents();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("aaab").setMimeType("text/plain")
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(m_api)
.createFile(m_api, changeSet, contents)
.setResultCallback(fileCallback);
}
};
final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (handleError(result.getStatus())) {
return;
}
m_file = result.getDriveFile();
m_file.openContents(m_api, DriveFile.MODE_READ_ONLY, null)
.setResultCallback(updateCallback);
}
};
final private ResultCallback<ContentsResult> updateCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
// Getting error here
if (handleError(result.getStatus())) {
return;
}
...
OK problem is that new file is not created.
for creating new file on GoogleDrive i use this code and work fine, problem is in your googleapiclient or i don't know where and how you call your funct createFile(). You must call in onConnected metode.
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
// create new contents resource
Drive.DriveApi.newContents(m_api)
.setResultCallback(contentsCallback);
}
final private ResultCallback<ContentsResult> contentsCallback = new
ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create new file contents");
return;
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("New file")
.setMimeType("text/plain")
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(m_api)
.createFile(m_api, changeSet, result.getContents())
.setResultCallback(fileCallback);
}
};
final private ResultCallback<DriveFileResult> fileCallback = new
ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the file");
return;
}
showMessage("Created a file: " + result.getDriveFile().getDriveId());
}
};
I've fixed it!
It had 2 problems:
First issue is my fault - my android manifest was missing APP_ID for drive services:
<meta-data
android:name="com.google.android.apps.drive.APP_ID"
android:value="#string/app_id" />
Second, the activity was extending predefined Google class: BaseGameActivity.
But this class assumes the I am not using Google drive files. To make it work, I had to add this in my Activity:
mRequestedClients = GameHelper.CLIENT_ALL;
and change Google predefined code. Actually it looks like a bug in google, assuming google APP folder is always used, and not Google drive files. In GameHelper.java, line 298, from:
builder.addScope(Drive.SCOPE_APPFOLDER);
to:
builder.addScope(Drive.SCOPE_FILE);
Related
I am working on application where i take a backup of some user's data to Google drive and in a later time i restore them.
The problem is that the files created and restored with no problems except that i can't see any progress, if i am uploading a large file it keep doing that in the background and can't notify the user that there is some operation happening in the background.
here is a snippet from the method i am using
Drive.DriveApi.newDriveContents(client)
.setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(#NonNull DriveApi.DriveContentsResult driveContentsResult) {
final DriveContents driveContents = driveContentsResult.getDriveContents();
File file = new File(filesToUpload.get(0).getURI());
// write content to DriveContents
OutputStream outputStream = driveContentsResult.getDriveContents().getOutputStream();
try {
outputStream.write(FileManagerUtils.getBytes(file));
} catch (IOException e) {
e.printStackTrace();
NotificationManger.dismissUploadingNotification();
NotificationManger.showSucessNotification(getApplicationContext(), R.string.notification_uploading_success);
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(obj.getFileName())
.build();
DriveId folderID = null;
// create a file on root folder
Drive.DriveApi.getFolder(client, folderID)
.createFile(client, changeSet, driveContents)
.setResultCallback(new ResultCallbacks<DriveFolder.DriveFileResult>() {
#Override
public void onSuccess(#NonNull DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.d(TAG, "Error while trying to create the file");
return;
}
Log.d(TAG, "Created a file with content: " + result.getDriveFile().getDriveId());
if (filesToUpload.size() > 0) {
filesToUpload.remove(0);
backup();
}
}
#Override
public void onFailure(#NonNull Status status) {
// show error
}
});
}
});
The problem is that if i'am uploading 3 files, the
Log.d(TAG,Log.d(TAG, "Created a file with content: " + result.getDriveFile().getDriveId()); is called very quickly after each others, and the actual files keep uploading in the background.
So can anyone tell me how to get real status of the uploading files in the background?
You need to add progress listeners which will listen to download or upload progress.
For uploading, you can use the MediaHttpUploaderProgressListener implementation given in Implementation details
public static class MyUploadProgressListener implements MediaHttpUploaderProgressListener {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
System.out.println("Initiation Started");
break;
case INITIATION_COMPLETE:
System.out.println("Initiation Completed");
break;
case MEDIA_IN_PROGRESS:
System.out.println("Upload in progress");
System.out.println("Upload percentage: " + uploader.getProgress());
break;
case MEDIA_COMPLETE:
System.out.println("Upload Completed!");
break;
}
}
}
For downloading, you can attach a DownloadProgressListener to inform users of the download progress in a ProgressDialog. As shown in Opening the file contents specifically in listening to the download progress, open the file contents with a DownloadProgressListener.
file.open(mGoogleClientApi, DriveFile.MODE_READ_ONLY, new DownloadProgressListener() {
#Override
public void onProgress(long bytesDownloaded, long bytesExpected) {
// display the progress
}
});
Solutions given in these SO post - Check progress for Upload & Download (Google Drive API for Android or Java) and How to show uploading to Google Drive progress in my android App? might help too.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
I'm trying to send the database file to Google Drive, I imported the libreries necessary, but when I try to upload the file to GoogleDrive I get NPE.
public class Main extends BaseDemoActivity {
private Drive service;
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
// create new contents resource
com.google.android.gms.drive.Drive.DriveApi.newDriveContents(getGoogleApiClient())
.setResultCallback(driveContentsCallback);
}
final private ResultCallback<DriveContentsResult> driveContentsCallback = new
ResultCallback<DriveContentsResult>() {
#Override
public void onResult(final DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create new file contents");
return;
}
new Thread() {
#Override
public void run() {
try {
java.io.File externalDB = new java.io.File(Environment.getExternalStorageDirectory(), getString(R.string.app_name) + "/data.crypt");
java.io.File fileContent = new java.io.File(externalDB.getPath());
FileContent mediaContent = new FileContent("application/x-sqlite3", fileContent);
com.google.api.services.drive.model.File body =
new com.google.api.services.drive.model.File();
body.setTitle("data.crypt");
body.setMimeType("application/x-sqlite3");
com.google.api.services.drive.model.File file =
service.files().insert(body, mediaContent).execute();
if (file != null) {
Log.d("GoogleDrive", "File Uploaded");
}
} catch (UserRecoverableAuthIOException e) {
//startActivityForResult(e.getIntent(), REQUEST_AUTHORISATION);
} catch (IOException e) {
e.printStackTrace();
}
}
}
.start();
}
};
...
...
The line of NPE
com.google.api.services.drive.model.File file =
service.files().insert(body, mediaContent).execute();
Log
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: FATAL EXCEPTION: Thread-34061
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: Process: chris.con, PID: 11372
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.api.services.drive.Drive$Files com.google.api.services.drive.Drive.files()' on a null object reference
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: at chris.con.Main$1$1.run(Main.java:51)
Well, the log message tells you that your private Drive service is not initialized.
The main issue here is that you're getting into a mess because you are mixing two independent APIs to work with Drive. One is the one from Google Play Sevices with package com.google.android.gms.drive and the other one is from the google-api-java with package com.google.api.services.drive. You should stick to only one of them and on my experience it should be the one of com.google.api.services. The one from Play Services has more bugs, less functionality, and you will often hit rate limits if you want data to be synced when you so require it instead of when the library feels like syncing the data.
You initialize the api.services Drive like this (for example)
GoogleAccountCredential credential = new GoogleAccountCredential(this, "oauth2:" + DriveScopes.DRIVE_FILE);
mDrive = new Drive.Builder(
new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("Your App")
.build();
After that you can service.files().insert. You don't need to "create contents" first with the com.google.api.services API.
I notice that there is not a conclusive answer to this on SO, so I am looking for a canonical answer to the question "How to check if a folder exists, and create it if it does not, using the Google Drive Android API?". Ideally showing examples of both the asynchronous approach using ResultCallback, and the synchronous approach using .await().
P.S. I am aware of this question with the same title, but the accepted answer is focussing on the known bug of lag on isTrashed(), and is not clear about at which point in the code you actually know the folder exists. Other answers seem out of date.
This question - while focussing on the laggy deletion status issue - does provide a pattern for testing if a folder exists.
Using an asynchronous callback:
Query query = new Query.Builder()
.addFilter(Filters.and(Filters.eq(
SearchableField.TITLE, "MyFolder"),
Filters.eq(SearchableField.TRASHED, false)))
.build();
Drive.DriveApi.query(getGoogleApiClient(), query)
.setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Cannot create folder in the root.");
} else {
boolean isFound = false;
for(Metadata m : result.getMetadataBuffer()) {
if (m.getTitle().equals("MyFolder")) {
showMessage("Folder exists");
isFound = true;
break;
}
}
if(!isFound) {
showMessage("Folder not found; creating it.");
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyFolder")
.build();
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFolder(getGoogleApiClient(), changeSet)
.setResultCallback(new ResultCallback<DriveFolder.DriveFolderResult>() {
#Override
public void onResult(DriveFolder.DriveFolderResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the folder");
} else {
showMessage("Created a folder");
}
}
});
}
}
}
});
Using a synchronous .await()
Query query = new Query.Builder()
.addFilter(Filters.and(Filters.eq(
SearchableField.TITLE, "MyFolder"),
Filters.eq(SearchableField.TRASHED, false)))
.build();
DriveApi.MetadataBufferResult result = Drive.DriveApi.query(getGoogleApiClient(), query)
.await();
if (!result.getStatus().isSuccess()) {
showMessage("Cannot create folder in the root.");
} else {
boolean isFound = false;
for(Metadata m : result.getMetadataBuffer()) {
if (m.getTitle().equals("MyFolder")) {
showMessage("Folder exists");
isFound = true;
break;
}
}
if(!isFound) {
showMessage("Folder not found; creating it.");
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyFolder")
.build();
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFolder(googleApiClient, changeSet).await();
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the folder");
} else {
showMessage("Created a folder");
}
}
Your quote:
... is not clear about at which point in the code you actually know the
folder exists
In the REST Api, you wait for a response from the 'ecexute()' method. Straightforward, and you can time the response out. You get folder/file id (ResourceId) and you know it exists in the Drive.
In GDAA, look at this answer. Again, when you get completion notification with a valid ResourceId, you know that the folder/file is 'up-there'.
Good Luck
i am using this code to retrieve the list of files and folders on Android, this returns only one file :(
{
Query query = new Query.Builder().setPageToken(mNextPageToken).build();
Drive.DriveApi.query(getGoogleApiClient(), query).setResultCallback(
metadataBufferCallback);
}
private final ResultCallback<MetadataBufferResult> metadataBufferCallback = new ResultCallback<MetadataBufferResult>() {
#Override
public void onResult(MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Problem while retrieving files");
return;
}
mResultsAdapter.append(result.getMetadataBuffer());
mNextPageToken = result.getMetadataBuffer().getNextPageToken();
}
};
The Android API uses Drive.File scope, which gives your app access to the specific files created by it or explicitly opened with it by the user. The query will return the subset of files you have access to that match the query.
I have been closely following the documentation for the Google Drive Android API and, all works great. I can create new text documents and read them back in using the mime type of text/plain.
What I cannot do is create a native Google "Document" or "Spreadsheet." Actually, I can create them by using the mime type to application/vnd.google-apps.document or application/vnd.google-apps.spreadsheet as per Supported MIME Types documentation.
If, however, I try to write content to these documents, the documents never get uploaded.
If I try to read documents that have content (content I created via a web browser) my openContents call fails.
Again, I can create text/plain documents and write to them, but they are not native Google Documents. I have scowered the documentation and sample files, but nothing describes what I'm looking for.
This seems so basic. Does the new GoogleApiClient not support doing this? What am I missing or doing wrong?
Here is the core code for creating. I have a similar issue when trying to read a application/vnd.google-apps.document but I'm sure the two issues are related. I'll spare the verbosity of "read" code.
private void exportToGDriveFile() {
Drive.DriveApi.newContents(getGoogleApiClient()).setResultCallback(createNewFileCallback);
}
final private ResultCallback<ContentsResult> createNewFileCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (!result.getStatus().isSuccess()) {
writeLog("Error while trying to create new file contents");
return;
}
String fileName = getIncrementedFileName();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(fileName)
.setMimeType("text/plain") // <-- This works! I can write and read back :)
//.setMimeType("application/vnd.google-apps.document") <-- can create if no contents are included.
//.setMimeType("application/vnd.google-apps.spreadsheet")
.setStarred(true)
.build();
writeLog("creating file: " + fileName);
// create a file on root folder
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFile(getGoogleApiClient(), changeSet, result.getContents())
.setResultCallback(afterCreateFileCallback);
}
};
private ResultCallback<DriveFileResult> afterCreateFileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
writeLog("Error while trying to create the file");
return;
}
DriveFile driveFile = result.getDriveFile();
writeLog("Created file " + driveFile.getDriveId());
new WriteFileAsyncTask().execute(driveFile);
}
};
private class WriteFileAsyncTask extends AsyncTask<DriveFile, Void, Boolean> {
#Override
protected Boolean doInBackground(DriveFile... args) {
DriveFile file = args[0];
try {
ContentsResult contentsResult = file.openContents(getGoogleApiClient(), DriveFile.MODE_WRITE_ONLY, null).await();
if (!contentsResult.getStatus().isSuccess()) {
return false;
}
/************************
If I try to write content here, `application/vnd.google-apps.document` files will not upload.
*************************/
String contents = "Hello World";
OutputStream outputStream = contentsResult.getContents().getOutputStream();
outputStream.write(contents.getBytes());
com.google.android.gms.common.api.Status status = file.commitAndCloseContents(
getGoogleApiClient(), contentsResult.getContents()).await();
return status.getStatus().isSuccess();
} catch (IOException e) {
// toast("IOException while appending to the output stream");
}
return false;
}
#Override
protected void onPostExecute(Boolean result) {
if (!result) {
// toast("Error while editing contents");
return;
}
// toast("Successfully uploaded Quizifications!");
}
}
It's not currently possible to read or edit the contents of Google Documents, Spreadsheets or Presentation files. They files are of a special type that don't have standard binary content, so you can't read and write from them in the same way you can from other files.
You can, however, interact with the metadata of existing files.
Sorry for the confusion, we should update the behavior so that its clear that its not possible.
Updating Google Docs with HTML is simple. Just make an api request with html-formatted text in the body (html tag is required) and content-type to be google docs, then your created/updated file will be available to the user as a Google Doc with all the formatting options.
request({
uri: 'https://www.googleapis.com/upload/drive/v2/files/'+fileId,
method: 'PUT',
qs: {
uploadType: 'media'
},
form: '<html> Hello <b>World!</b> </html>',
headers: {
'Content-Type': 'application/vnd.google-apps.document',
'Authorization': 'Bearer ' + access_token
}}, function (error, response, body){
})