Reputation: 5986
I want to programmatically access a specific Excel spreadsheet which will be included in my project folder and upload it to Google Drive. I am including the spreadsheet within my src folder and using the following code:
private void saveFileToDrive() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
URL fileURL = getClass().getClassLoader().getResource("Untitled spreadsheet.xlsx");
String filePath2 = fileURL.getPath();
java.io.File fileContent = new java.io.File(filePath2);
FileContent mediaContent = new FileContent("application/vnd.ms-excel", fileContent);
File body = new File();
body.setTitle(fileContent.getName());
body.setMimeType("application/vnd.ms-excel");
File file = service.files().insert(body, mediaContent).setConvert(true).execute();
if (file != null) {
showToast("File uploaded: " + file.getTitle());
}
else
;
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
}
However, I keep getting the following FileNotFoundException:
11-29 14:43:45.189: W/System.err(21133): java.io.FileNotFoundException: /file:/data/app/com.example.drivequickstart-2.apk!/Untitled spreadsheet.xlsx: open failed: ENOENT (No such file or directory)
Does anyone know what is causing this?
EDIT: I have tried to modify my code as follows in line with suggestions below:
private void saveFileToDrive() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
String mime = "application/vnd.ms-excel";
InputStream in = getApplicationContext().getAssets().open("Untitled spreadsheet.xlsx");
InputStreamContent content = new InputStreamContent(mime, in);;
File body = new File();
body.setTitle("Untitled spreadsheet");
body.setMimeType("application/vnd.ms-excel");
File file = service.files().insert(body, content).setConvert(true).execute();
if (file != null) {
showToast("File uploaded: " + file.getTitle());
}
else
;
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
}
However, this gives me the following error:
11-29 20:31:08.118: E/AndroidRuntime(9833): java.lang.IllegalArgumentException
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.common.base.Preconditions.checkArgument(Preconditions.java:76)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.api.client.googleapis.media.MediaHttpUploader.getMediaContentLength(MediaHttpUploader.java:328)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.api.client.googleapis.media.MediaHttpUploader.executeUploadInitiation(MediaHttpUploader.java:347)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.api.client.googleapis.media.MediaHttpUploader.upload(MediaHttpUploader.java:266)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:408)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:328)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:449)
11-29 20:31:08.118: E/AndroidRuntime(9833): at com.example.drivequickstart.MainActivity$3.run(MainActivity.java:303)
11-29 20:31:08.118: E/AndroidRuntime(9833): at java.lang.Thread.run(Thread.java:864)
and points to the line: File file = service.files().insert(body, content).setConvert(true).execute();
Upon closer inspection, I discovered that the length of the InputStreamContent is -1, so the problem probably originates there.
Upvotes: 2
Views: 1727
Reputation: 20885
You use the Java idiom ClassLoader.getResource()
to load resources from the CLASSPATH
. However, that's not how we do it on Android.
You'll place your file in the assets/
directory or in res/raw/
, and then retrieve it with the AssetManager
or an identifier like R.raw.untitled_spreadsheet.xlsx
.
See the official guide:
While uncommon, you might need access your original files and directories. If you do, then saving your files in res/ won't work for you, because the only way to read a resource from res/ is with the resource ID. Instead, you can save your resources in the assets/ directory.
Files saved in the assets/ directory are not given a resource ID, so you can't reference them through the R class or from XML resources. Instead, you can query files in the assets/ directory like a normal file system and read raw data using AssetManager.
However, if all you require is the ability to read raw data (such as a video or audio file), then save the file in the res/raw/ directory and read a stream of bytes using openRawResource().
Speaking in code, if you put the file in assets/sheet.xlsx
:
String mime = "application/vnd.ms-excel";
InputStream in = ctx.getAssets().open("sheet.xlsx");
InputStreamContent content = new InputStreamContent(mime, in);
where ctx
is a Context
. Since Activity
is a Context
you can skip the ctx
and directly call getAssets()
if you happen to write this code inside an Activity
.
Notice I didn't use a FileContent
: instead I choosed InputStreamContent
because you don't really have a java.io.File
object, but an input stream.
Basically, you are asking the Drive client: "Read bytes from this local stream, upload them and make them accessible under the name $NAME". The name $NAME
is supplied to the service via a com.google.api.services.drive.model.File
object's title
field, which can be whatever you like - I think it can include directory separators, too.
This is a link to the Javadoc for Google Drive V2
Upvotes: 1
Reputation: 2186
To tackle your question of "What is causing this?", refer to the discussion here (around comment #13).
It's a simple reason that you are not giving it a valid file path. You are getting a resource from the classloader, and that resource is coming from a jar (which is actually never unpacked into the filesystem).
So when you ask the URL of the resource for the path with url.getPath() you get back a (in your case) file:/data/app/com.example.drivequickstart-2.apk!/Untitled spreadsheet.xlsx
this is not a legal 'file:' url string!
He also suggests a very apt way to tackle this:
What you should do is just feed to the constructors the String returned by URL.toString(). What you should get back then is some sort of registered url type that still points into the jar file, but the url resolver in your VM knows how to handle.
Upvotes: 0