Che*_*eng 58 android google-drive-api google-drive-android-api
目前,我正在使用Google Drive Android API将我的Android应用数据存储到Google云端硬盘应用文件夹.
这是我在保存应用程序数据时正在做的事情
这是执行上述操作的代码.
public static boolean saveToGoogleDrive(GoogleApiClient googleApiClient, File file, HandleStatusable h, PublishProgressable p) {
// Should we new or replace?
GoogleCloudFile googleCloudFile = searchFromGoogleDrive(googleApiClient, h, p);
try {
p.publishProgress(JStockApplication.instance().getString(R.string.uploading));
final long checksum = org.yccheok.jstock.gui.Utils.getChecksum(file);
final long date = new Date().getTime();
final int version = org.yccheok.jstock.gui.Utils.getCloudFileVersionID();
final String title = getGoogleDriveTitle(checksum, date, version);
DriveContents driveContents;
DriveFile driveFile = null;
if (googleCloudFile == null) {
DriveApi.DriveContentsResult driveContentsResult = Drive.DriveApi.newDriveContents(googleApiClient).await();
if (driveContentsResult == null) {
return false;
}
Status status = driveContentsResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
driveContents = driveContentsResult.getDriveContents();
} else {
driveFile = googleCloudFile.metadata.getDriveId().asDriveFile();
DriveApi.DriveContentsResult driveContentsResult = driveFile.open(googleApiClient, DriveFile.MODE_WRITE_ONLY, null).await();
if (driveContentsResult == null) {
return false;
}
Status status = driveContentsResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
driveContents = driveContentsResult.getDriveContents();
}
OutputStream outputStream = driveContents.getOutputStream();
InputStream inputStream = null;
byte[] buf = new byte[8192];
try {
inputStream = new FileInputStream(file);
int c;
while ((c = inputStream.read(buf, 0, buf.length)) > 0) {
outputStream.write(buf, 0, c);
}
} catch (IOException e) {
Log.e(TAG, "", e);
return false;
} finally {
org.yccheok.jstock.file.Utils.close(outputStream);
org.yccheok.jstock.file.Utils.close(inputStream);
}
if (googleCloudFile == null) {
// Create the metadata for the new file including title and MIME
// type.
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setTitle(title)
.setMimeType("application/zip").build();
DriveFolder driveFolder = Drive.DriveApi.getAppFolder(googleApiClient);
DriveFolder.DriveFileResult driveFileResult = driveFolder.createFile(googleApiClient, metadataChangeSet, driveContents).await();
if (driveFileResult == null) {
return false;
}
Status status = driveFileResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
} else {
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setTitle(title).build();
DriveResource.MetadataResult metadataResult = driveFile.updateMetadata(googleApiClient, metadataChangeSet).await();
Status status = metadataResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
}
Status status;
try {
status = driveContents.commit(googleApiClient, null).await();
} catch (java.lang.IllegalStateException e) {
// java.lang.IllegalStateException: DriveContents already closed.
Log.e(TAG, "", e);
return false;
}
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
status = Drive.DriveApi.requestSync(googleApiClient).await();
if (!status.isSuccess()) {
// Sync request rate limit exceeded.
//
//h.handleStatus(status);
//return false;
}
return true;
} finally {
if (googleCloudFile != null) {
googleCloudFile.metadataBuffer.release();
}
}
}
Run Code Online (Sandbox Code Playgroud)
private static String getGoogleDriveTitle(long checksum, long date, int version) {
return "jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=" + checksum + "-date=" + date + "-version=" + version + ".zip";
}
// https://stackoverflow.com/questions/1360113/is-java-regex-thread-safe
private static final Pattern googleDocTitlePattern = Pattern.compile("jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=([0-9]+)-date=([0-9]+)-version=([0-9]+)\\.zip", Pattern.CASE_INSENSITIVE);
private static GoogleCloudFile searchFromGoogleDrive(GoogleApiClient googleApiClient, HandleStatusable h, PublishProgressable p) {
DriveFolder driveFolder = Drive.DriveApi.getAppFolder(googleApiClient);
// https://stackoverflow.com/questions/34705929/filters-ownedbyme-doesnt-work-in-drive-api-for-android-but-works-correctly-i
final String titleName = ("jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=");
Query query = new Query.Builder()
.addFilter(Filters.and(
Filters.contains(SearchableField.TITLE, titleName),
Filters.eq(SearchableField.TRASHED, false)
))
.build();
DriveApi.MetadataBufferResult metadataBufferResult = driveFolder.queryChildren(googleApiClient, query).await();
if (metadataBufferResult == null) {
return null;
}
Status status = metadataBufferResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return null;
}
MetadataBuffer metadataBuffer = null;
boolean needToReleaseMetadataBuffer = true;
try {
metadataBuffer = metadataBufferResult.getMetadataBuffer();
if (metadataBuffer != null ) {
long checksum = 0;
long date = 0;
int version = 0;
Metadata metadata = null;
for (Metadata md : metadataBuffer) {
if (p.isCancelled()) {
return null;
}
if (md == null || !md.isDataValid()) {
continue;
}
final String title = md.getTitle();
// Retrieve checksum, date and version information from filename.
final Matcher matcher = googleDocTitlePattern.matcher(title);
String _checksum = null;
String _date = null;
String _version = null;
if (matcher.find()){
if (matcher.groupCount() == 3) {
_checksum = matcher.group(1);
_date = matcher.group(2);
_version = matcher.group(3);
}
}
if (_checksum == null || _date == null || _version == null) {
continue;
}
try {
checksum = Long.parseLong(_checksum);
date = Long.parseLong(_date);
version = Integer.parseInt(_version);
} catch (NumberFormatException ex) {
Log.e(TAG, "", ex);
continue;
}
metadata = md;
break;
} // for
if (metadata != null) {
// Caller will be responsible to release the resource. If release too early,
// metadata will not readable.
needToReleaseMetadataBuffer = false;
return GoogleCloudFile.newInstance(metadataBuffer, metadata, checksum, date, version);
}
} // if
} finally {
if (needToReleaseMetadataBuffer) {
if (metadataBuffer != null) {
metadataBuffer.release();
}
}
}
return null;
}
Run Code Online (Sandbox Code Playgroud)
在加载应用程序数据期间出现问题.想象一下以下操作
12345
.正在使用的文件名是...checksum=12345...zip
...checksum=12345...zip
.下载内容.验证内容的校验和也是12345
如此.67890
.现有的应用程序文件夹zip文件已重命名为...checksum=67890...zip
...checksum=67890...zip
.但是,下载内容后,内容的校验和仍然是旧的12345
!public static CloudFile loadFromGoogleDrive(GoogleApiClient googleApiClient, HandleStatusable h, PublishProgressable p) {
final java.io.File directory = JStockApplication.instance().getExternalCacheDir();
if (directory == null) {
org.yccheok.jstock.gui.Utils.showLongToast(R.string.unable_to_access_external_storage);
return null;
}
Status status = Drive.DriveApi.requestSync(googleApiClient).await();
if (!status.isSuccess()) {
// Sync request rate limit exceeded.
//
//h.handleStatus(status);
//return null;
}
GoogleCloudFile googleCloudFile = searchFromGoogleDrive(googleApiClient, h, p);
if (googleCloudFile == null) {
return null;
}
try {
DriveFile driveFile = googleCloudFile.metadata.getDriveId().asDriveFile();
DriveApi.DriveContentsResult driveContentsResult = driveFile.open(googleApiClient, DriveFile.MODE_READ_ONLY, null).await();
if (driveContentsResult == null) {
return null;
}
status = driveContentsResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return null;
}
final long checksum = googleCloudFile.checksum;
final long date = googleCloudFile.date;
final int version = googleCloudFile.version;
p.publishProgress(JStockApplication.instance().getString(R.string.downloading));
final DriveContents driveContents = driveContentsResult.getDriveContents();
InputStream inputStream = null;
java.io.File outputFile = null;
OutputStream outputStream = null;
try {
inputStream = driveContents.getInputStream();
outputFile = java.io.File.createTempFile(org.yccheok.jstock.gui.Utils.getJStockUUID(), ".zip", directory);
outputFile.deleteOnExit();
outputStream = new FileOutputStream(outputFile);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
} catch (IOException ex) {
Log.e(TAG, "", ex);
} finally {
org.yccheok.jstock.file.Utils.close(outputStream);
org.yccheok.jstock.file.Utils.close(inputStream);
driveContents.discard(googleApiClient);
}
if (outputFile == null) {
return null;
}
return CloudFile.newInstance(outputFile, checksum, date, version);
} finally {
googleCloudFile.metadataBuffer.release();
}
}
Run Code Online (Sandbox Code Playgroud)
首先,我想
Status status = Drive.DriveApi.requestSync(googleApiClient).await()
Run Code Online (Sandbox Code Playgroud)
不能很好地完成这项工作.它在大多数情况下失败,出现错误信息Sync request rate limit exceeded.
事实上,强加的硬限制requestSync
,使得API不是特别有用 - Android Google Play/Drive Api
但是,即使requestSync
成功,loadFromGoogleDrive
仍然只能获取最新的文件名,但过时的校验和内容.
我100%肯定loadFromGoogleDrive
会给我一个缓存的数据内容,并提供以下观察结果.
DownloadProgressListener
in driveFile.open
,bytesDownloaded是0,bytesExpected是-1.loadFromGoogleDrive
将能够获得具有正确校验和内容的最新文件名.有没有强大的方法,以避免始终从Google云端硬盘加载缓存的应用数据?
我设法制作一个演示.以下是重现此问题的步骤.
https://github.com/yccheok/google-drive-bug
文件名为"123.TXT"的文件,内容"123"将在app文件夹中创建.
之前的文件将重命名为"456.TXT",内容更新为"456"
找到文件名为"456.TXT"的文件,但读取先前缓存的内容"123".我期待内容"456".
请注意,如果我们
我已正式提交问题报告 - https://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=4727
这就是我的设备下的样子 - http://youtu.be/kuIHoi4A1c0
我意识到,并非所有用户都会遇到这个问题.例如,我曾使用另一款Nexus 6,Google Play Services 9.4.52(440-127739847)进行测试.问题没有出现.
我编译了一个APK用于测试目的 - https://github.com/yccheok/google-drive-bug/releases/download/1.0/demo.apk
归档时间: |
|
查看次数: |
1783 次 |
最近记录: |