Drive Api save to folder, if folder doesnt exist, create than save

谁说胖子不能爱 提交于 2020-01-05 09:36:30

问题


im using the Google Drive API to save(use as backup) a database there, its working nice, but just if i use the ROOT

the Api Call:

  MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                           ......build();


Drive.DriveApi.getRootFolder(mGoogleApiClient)
                        .createFile(mGoogleApiClient, metadataChangeSet, result.getDriveContents())
                        .setResultCallback(fileCallback);

CallBack to Save the file:

final public ResultCallback < DriveFolder.DriveFileResult > fileCallback = new
            ResultCallback < DriveFolder.DriveFileResult > () {
                @Override
                public void onResult(DriveFolder.DriveFileResult result) {

                    if (!result.getStatus().isSuccess()) {


                        return;
                    }

                    Log.i(TAG, "Successfull !");
                }
            };

i know that i must get the Folder, but if i do this, i need to do a CallBack to call another callback and then save?

isnt any way to directly do .createNewFile inside the FOLDER? without doing another Query for folder, check if the folder exist than create the folder, than use the DriveID, than create the file?


回答1:


Remember, that in the GooDrive universe, the tree structure (folder, subfolder, ...) is a mirage. The Drive is a flat system of objects (files, folders) where one of the metadata fields is a 'set of parent IDs', that actually forms the notion of parentobject - childobject structure. Actually the classic tree (one parent many children) is not even enforced, so a child object can 'appear' in more that one parent.
This fact explains that you CAN NOT create an OS type of path in one shot. The objects (parents) must be created before their IDs can be plugged into child objects' metadata.

So the only way to do it, is to do what you say:

    if folder exists
      return it's ID
    else
      return ID of newly created one

   create a child object with parent's ID

... and here is an example how I create a structure of type:

   / MYROOT / 2015 / 2015-12

(where MYROOT, 2015 , 2015-12 are subfloders the Drive root)

new Thread(new Runnable() {
  @Override
  public void run() {
    DriveId Id = getFolder( getFolder( getFolder(
          Drive.DriveApi.getRootFolder(mGAC).getDriveId(), "MYROOT"),
        "2015",
      "2015-12"
    );
  }
}).start();

GoogleApiClient mGAC;

DriveId getFolder(DriveId parentId, String titl) {
  DriveId dId = null;
  if (parentId != null && titl != null) try {
    ArrayList<Filter> fltrs = new ArrayList<>();
    fltrs.add(Filters.in(SearchableField.PARENTS, parentId));
    fltrs.add(Filters.eq(SearchableField.TITLE, titl));
    fltrs.add(Filters.eq(SearchableField.MIME_TYPE, "application/vnd.google-apps.folder"));
    Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();

    MetadataBuffer mdb = null;
    DriveApi.MetadataBufferResult rslt = Drive.DriveApi.query(mGAC, qry).await();
    if (rslt.getStatus().isSuccess()) try {
      mdb = rslt.getMetadataBuffer();
      if (mdb.getCount() > 0)
        dId = mdb.get(0).getDriveId();
    } catch (Exception ignore) {}
    finally { if (mdb != null) mdb.close(); }

    if (dId == null) {
      MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(UT.MIME_FLDR).build();
      DriveFolderResult r1 = parentId.asDriveFolder().createFolder(mGAC, meta).await();
      DriveFolder dFld = (r1 != null) && r1.getStatus().isSuccess() ? r1.getDriveFolder() : null;
      if (dFld != null) {
        MetadataResult r2 = dFld.getMetadata(mGAC).await();
        if ((r2 != null) && r2.getStatus().isSuccess()) {
          dId = r2.getMetadata().getDriveId();
        }
      }
    }
  } catch (Exception e) { e.printStackTrace(); }
  return dId;
}

In the 'mdb.get(0).getDriveId()' area, you can see how hacky it gets when you try to impose a classic tree structure on the Drive. The search here can return multiple objects with the same name, so I use the first one. There should be some kind of error reporting here.

As you can see it is possible to replace callbacks with the 'await()' method, flattening the code into a classic DOS style spaghetti code as long as you place the whole sequence off-UI thread (asynctask, thread, ....)

Still, more elegant (IMO) option to accomplish this is to use recursive call from the result callback.

  fromPath(Drive.DriveApi.getRootFolder(mGAC).getDriveId(), "MYROOT/2015/2015-12/file.jpg");
  ....
  void fromPath(final DriveId parentId, final String path) {
    if (parentId != null && path != null) {

      final int idx = path.indexOf('/');
      if (idx < 0) {
        // reached last path item - probably file name
        // CREATE FILE WITH patentID AND QUIT
        return;    //--- DONE -------------------->>>
      }

      final String titl = path.substring(0, idx);

      ArrayList<Filter> fltrs = new ArrayList<>();
      fltrs.add(Filters.in(SearchableField.PARENTS, parentId));
      fltrs.add(Filters.eq(SearchableField.TITLE, titl));
      fltrs.add(Filters.eq(SearchableField.MIME_TYPE, UT.MIME_FLDR));
      Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();

      Drive.DriveApi.query(mGAC, qry).setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
        @Override
        public void onResult(DriveApi.MetadataBufferResult rslt) {
          MetadataBuffer mdb = null;
          if (rslt != null && rslt.getStatus().isSuccess()) {
            try {
              mdb = rslt.getMetadataBuffer();
              for (Metadata md : mdb) {
                if (md.isTrashed()) continue;
                fromPath(md.getDriveId(), path.substring(idx + 1));
                return; //+++ first found, NEXT +++++++>>>
              }
            } finally { if (mdb != null) mdb.close(); }
          }

          MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(UT.MIME_FLDR).build();
          parentId.asDriveFolder().createFolder(mGAC, meta)
          .setResultCallback(new ResultCallback<DriveFolderResult>() {
            @Override
            public void onResult(DriveFolderResult rslt) {
              DriveFolder dFld = rslt != null && rslt.getStatus().isSuccess() ? rslt.getDriveFolder() : null;
              if (dFld != null) {
                dFld.getMetadata(mGAC).setResultCallback(new ResultCallback<MetadataResult>() {
                  @Override
                  public void onResult(MetadataResult rslt) {
                    if (rslt != null && rslt.getStatus().isSuccess()) {
                      fromPath(rslt.getMetadata().getDriveId(), path.substring(idx + 1));
                      return; //+++ created, NEXT +++++++>>>
                    }
                  }
                });
              }
            }
          });
        }
      });
    }
  }

A WORD OF CAUTION:
As I called this sequence repeatedly, using the last DriveId (like 2015-12) as a parent of a JPEG image file, I have experienced weird behavior, like suddenly getting a 'null' result from 'Drive.DriveApi.getRootFolder(mGAC).getDriveId()'. It shouldn't happen and I assume it is a bug in GDAA. I contribute this to the fact that the DriveId used inside GDAA is 'invalid' until the folder gets committed and the ResourceId is resolved in underlying REST Api. Unfortunately, there is no completion event available for folder creation, so I resolved this by calling this sequence only once in onConnected() and caching the '2015-12's DriveId for later use as a parent of the image JPEG files. Actually you can see it here (createTree() method) with text file on the tail, but the moment I switched the TEXT to JPEG, all hell broke lose.

Good Luck



来源:https://stackoverflow.com/questions/34053840/drive-api-save-to-folder-if-folder-doesnt-exist-create-than-save

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!