-->

403 quotaExceeded error when executing com.google.

2020-07-24 05:57发布

问题:

I have an Android App which copies a spreadsheet, edit the spreadsheet and upload some photos to a third person Google Drive Account, using a service account.

The App was working OK. But the last 22nd of January, the App started to fail.

Now it copies the spreadsheet and edit, but the photos are no longer uploaded. And I'm receiving this error.

com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden

{re 
  "code" : 403,
  "errors" : [ {
    "domain" : "usageLimits",
    "message" : "The user has exceeded their Drive storage quota",
    "reason" : "quotaExceeded"
  } ],
  "message" : "The user has exceeded their Drive storage quota"
}

I have checked both the API quota and the Drive storage quota and they are quite lower than the limit.

I have not made changes in the code and I have not changed the security settings in any of the involved accounts.

I could not find any known issue about this.

Is anyone else experiencing something similar??

回答1:

I found the problem and a candidate solution.

This is the scenario:

  • An account with acces to Google Drive, let's call it: manager@gmail.com.
  • Several accounts which are used to upload photos to the manager Google Drive, using an Android app, let's call it: worker@gmail.com.
  • I don't want to give access to the workers@gmail.com to the Google Drive of the manager@gmail.com, so I created a service account, which is the responsible to upload the photos from the Android App to the manager Google Drive, let's call it service@gmail.com.

When worker@gmail.com takes a photo, the service@gmail.com uploads a photo to the manager@gmail.com Google Drive. But the owner of the photo is the service@gmail.com. So in the manager@gmail.com Google Drive, you can see all the photos, but the quota always remains to zero.

After uploading 15 GB o photos I actually exceeded the quota, but it was the quota of the service@gmail.com (which is not visible in Google Drive).

SOLUTIONS

  1. Log into the manager@gmail.com Google Drive and delete the photos.

    ERROR: manager@gmail.com is not the owner. So if you remove the file, it will not be visible to the manager but it will keep on consuming quota of the service@gmail.com.

  2. Log into the service@gmail.com Google Drive and delete the photos.

    ERROR: There is no way (AKAIK) to log into a service@gmail.com Google Drive.

  3. Transfer the ownership of the files from service@gmail.com to manager@gmail.com.

    COOL That's a valid solution. Let's do it!!

    ERROR It seems that Google does not allow to change the ownership of some types of files. More info here

DIRTY SOLUTION

Yep, this may not be the most elegant solution. But it worked for me.

I created a Google Script using the manager@gmail.com account and I published as a Public Web Application.

function doGet(data) {
  var fileId = data.parameter.fileId;
  var file = DriveApp.getFileById(fileId);
  var result = { fileId: file.makeCopy(file.getName()).getId() };
  //I need to remove the file. Otherwise it semains linked to the one created by the *service@gmail.com*
  DriveApp.removeFile(file);
  return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);
}

This methods receives the fileId created by the service@gmail.com, makes a copy (which owns manager@gmail.com). I need to remove the the file created by the service@gmail.com. The file is actually not removed, but it is not linked any more to the one created by the manager@gmail.com. Finally it returns the new fileId.

Now, my Android App looks like this:

private void uploadPhoto(String photoFolderId, File body, FileContent mediaContent) {
    //First, I upload the photo
    if(driveService.files().insert(body, mediaContent) != null) {
        transferOwnerShip(photoFolderId);
    }

    private void transferOwnerShip(String photoFolderId) {
        String photoId = getInsertedPhotoId();
        //Now I create a copy of the photo. The copied photo is owned by *manager@gmail.com*
        String ownedPhotoId = makeCopyOfPhoto(photoId);
        if(ownedPhotoId != null)
            //Once I have copied the photo, I remove the photo owned by *service@gmail.com*
            driveService.files().delete(photoId).execute();
    }

    private String makeCopyOfFile(String fileId) {
        URL url;
        try {
            url = new URL(URL_TO_MY_PUBLIC_SERVICE + "?fileId=" + fileId);

            DefaultHttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(url.toURI());
            HttpResponse resp = client.execute(get);
            BufferedReader rd = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
            StringBuffer result = new StringBuffer();
            String line = "";
            while ((line = rd.readLine()) != null) {
                result.append(line);
            }
            JSONObject jsonResponse = new JSONObject(result.toString());
            return jsonResponse.getString("fileId");
        } catch (Exception e) {
            warnAboutError();
        }
        return null;
    }

}

The dirty solution works, but it is very dirty. I could not figure out other way to work around this.