2

I'm using the code from here: https://developers.google.com/drive/api/v3/manage-downloads#downloading_a_file

The code snippet I'm using is the following and placed in the main method:

    String fileId = "some file ID";
    OutputStream outputStream = new ByteArrayOutputStream();
    driveService.files().get(fileId)
    .executeMediaAndDownloadTo(outputStream);

I have found no sign of the code actually downloading the file, nor do I know where the file is IF it actually downloads. I'm not sure if I am using the proper scope to gain permission to download files. I am able to upload, list, and delete files as long as I know the fileID, but downloading seems to not work.

    private static final List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE);

Alternatively, I'm trying to create a method to enact the download protocol like so:

    private static void downloadFile(Drive service, File file (or String fileID)){
    }

but am not sure on how to do so. I've tried looking for samples online but most are from v1 or v2 apis and don't seem to work for me.

Also, I've read somewhere that it is not possible to download a Folder. Instead, I have to download each item in the folder one by one. So do I have to make an Arraylist/list/array of the fileIDs and iterate through it after initializing a variable to represent fileID?

Edit: Some progress has been made, but I still have some problems I'm trying to thrash out.

    List<File> files = result.getFiles();
    File newFile;
    if (files == null || files.isEmpty()) {
        System.out.println("No files found.");
    } else {
        System.out.println("Files:");
        for (File file : files) {
            System.out.printf("%s (%s)\n", file.getName(), file.getId());
            String fileId = file.getId();
            //System.out.println(fileId);
            String fileName = file.getName();
            //System.out.println(fileName);
            OutputStream outputstream = new FileOutputStream();
            service.files().get(fileId)
            .executeMediaAndDownloadTo(outputstream);
            outputstream.flush();
            outputstream.close();
    }

What I want: The above code is in the main method. I don't know if this is the proper way to do it, but as the program fetches each file and executes the System.out.printf, I also want it to download that file (with the same mimeType, and pref the same name too) into the destination set in the OutputStream constructor (C://User//some name//Downloads).

What I've tried: From what I've tested, it only downloads the first file exactly the way I want, but only because I specify the name and extension in OutputStream. I've initialized variables 'fileId' and 'fileName' so that they will change according to the info as the program fetches the metadata for the next file, but I don't know how to change or set multiple constructors into this code:

    OutputStream outputstream = new FileOutputStream();
            service.files().get(fileId)
            .executeMediaAndDownloadTo(outputstream);

to download all the files.

My folder hierarchy in Google Drive is like this:

Logs

-- bin (folder)

---- bunch of .bin files

-- .xml file

-- .xml file

Psi
  • 61
  • 10

1 Answers1

3

You are using a ByteArrayOutputStream object as the output of your download. If your program terminates without having saved the contents of this object somewhere, you will not be able to find this information in your computer's disk, as it is not written to it but rather saved in memory as a buffered byte-array (refer to the previous link for more information).

If you want to save the output of the download to the file, I suggest you use instead a FileOutputStream as the destination of your download. In order to do that, you have to modify your code as follows:

  1. Add the appropriate import declaration:

    import java.io.FileOutputStream;
    
  2. Modify your outputStream variable assignment as follows:

    OutputStream outputStream = new FileOutputStream('/tmp/downloadedfile');
    

    Where the parameter passed to FileOutputStream should be the desired destination path of your download.

  3. After writing any contents to your file, add the following lines of code:
    outputStream.flush();
    outputStream.close();
    
    This will ensure that your file is being written to properly.

In regards to downloading a folder, you are completely right - you will first need to fetch the folder you want to download, and each of their children. In order to better understand how to do it, I suggest you check out the following answer: Download folder with Google Drive API

Edit - example downloading a folder

String destinationFolder = "/tmp/downloadedfiles/";
List<File> files = result.getFiles();
File newFile;
if (files == null || files.isEmpty()) {
  System.out.println("No files found.");
} else {
  System.out.println("Files:");
  for (File file : files) {
    System.out.printf("%s (%s)\n", file.getName(), file.getId());
    String fileId = file.getId();
    String fileName = file.getName();
    OutputStream outputstream = new FileOutputStream(destinationFolder + fileName);
    service.files().get(fileId)
           .executeMediaAndDownloadTo(outputstream);
    outputstream.flush();
    outputstream.close();
  }
}

carlesgg97
  • 4,184
  • 1
  • 8
  • 24
  • Thanks! The code worked, and I found the downloaded file in the designated path. Is it possible to change the code so it automatically detects the file type in Drive and changes the downloadedfile to that format? For now, I have to manually designated the downloadedfile type to .png, .jpg, .txt, etc. – Psi Nov 20 '19 at 15:12
  • @Phanotak From the file you obtain from the API using `driveService.files().get(fileId)`, you can call the `getMimeType()` method. That will give you, as the name says, the [Mime Type](https://en.wikipedia.org/wiki/Media_type) of the file. Afterwards, you could map this result to its appropriate extension (there may be Java libraries or datasets that could help you do that). Cheers – carlesgg97 Nov 20 '19 at 16:03
  • @Phanotak you may want to check this answer: https://stackoverflow.com/questions/48053127/convert-mimetype-to-extension – carlesgg97 Nov 20 '19 at 16:54
  • when I download a file, how can I keep the same file name? I assume I have to change something in 'OutputStream outputStream2 = new FileOutputStream' – Psi Nov 20 '19 at 18:31
  • @Phanotak when you obtain the file, using `driveService.files().get(fileId)`, you can call `getName()` (see [in the documentation](https://developers.google.com/resources/api-libraries/documentation/drive/v3/java/latest/com/google/api/services/drive/model/File.html#getName--)). Afterwards, when constructing your `OutputStream` object, you can pass that name to it. – carlesgg97 Nov 21 '19 at 08:09
  • is there sample code or something I can see as an example? From what I've seen, the constructors when using OutputStream are static and won't change once set (download path, name, extension). Edited my question to reflect progress and new issues. Thanks!! – Psi Nov 21 '19 at 23:33
  • @Phanotak I have edited my answer including an example based on your code. For each file, a new `OutputStream` gets created, so you can set them the filename you want. Additionally, you may get the appropriate file extension using any of the solutions presented [here](https://stackoverflow.com/questions/48053127/convert-mimetype-to-extension) and append it to the final filename. Should you have any other doubt, please consider opening a brand-new Stackoverflow question. Thanks! – carlesgg97 Nov 22 '19 at 11:39
  • thanks!! I couldn't find anywhere that really explained the constructors you can use with FileOutputstream. Most of the examples I found had path and file name already set for singular use. This part was really hindering me – Psi Nov 22 '19 at 17:57
  • @Phanotak haha no worries. Glad my answer was helpful to you :) – carlesgg97 Nov 22 '19 at 18:29