Search code examples
javaspringspring-mvcspring-socialspring-social-google

How do you download a file with spring social google?


I'm attempting to use the spring social google library with the driveOperations() implementation. Whenever I attempt to download a file, I get an error. I've even tried to download publicly shared files with no success. Authentication is working.

I've tried the following variations:

  Resource resource = google.driveOperations().downloadFile("uniquedocidhere");
 // another approach, no error, but getDownloadUrl() is null
  DriveFile driveFile = google.driveOperations().getFile("uniqeidhere");
  google.driveOperations().downloadFile(driveFile); // 404 
// finally trying it with a file drive v2 url gives an invalid URI error

I added drive to the scope so I know that isn't an issue. I'm prompted for drive access when I initially authenticated with the application.

The files in question are google spreadsheets, but I was hoping to have them download as excel files. With stock google SDK, this can be done by getting the exportLinks and then fetching from that URL. While the spring social google library has

  final Map<String, String> links = driveFile.getExportLinks();

The export links can't be used because downloadFile doesn't seem to work with the URLs here (or perhaps they are also null).

Does anyone know how to get a file to download with spring social google and drive? I haven't found any sample code that covers drive, only google plus and tasks.

Currently using Spring Boot 1.3.3 / Spring 4.2.5 with Spring Social Google 1.0.0 RELEASE


Solution

  • Spring social google does not support downloading Google Docs files as they're not in a useful format. There isn't a property on the metadata to download the file like there would be for other file types.

    While Google's own SDK can handle downloading exported formats such as Excel, CSV and PDF, spring social google only exposes the getExportedLinks() property to see the URLs and types but offers no method to actually download them.

    I'm now looking into the possibility of either forking it and adding methods to call the Google drive v2 export endpoint or to get the access token so that I can use the stock google sdk to fetch the files.

    __

    I found that I could access the access token by calling google.getAccessToken() where Google is created in my social configuration as

    @Bean
        @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
        public Google google(final ConnectionRepository repository) {
            final Connection<Google> connection = repository.findPrimaryConnection(Google.class);
            if (connection == null)
                log.debug("Google connection is null");
            else
                log.debug("google connected");
            return connection != null ? connection.getApi() : null;
        }
    

    I then was able to download the file with this:

    public class LinkedTemplate extends AbstractOAuth2ApiBinding {
        private String accessToken;
    
        public LinkedTemplate() {
        }
    
        public LinkedTemplate(String accessToken) {
            super(accessToken);
            this.accessToken = accessToken;
        }
    
        class ExcelConverter extends ByteArrayHttpMessageConverter {
            public ExcelConverter() {
                MediaType t = new MediaType("application", "vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                this.setSupportedMediaTypes(Arrays.asList(t));
            }
        }
    
        public void downloadLinkedFile(final String url, final String outputPath) throws IOException {
            //application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
            RestTemplate template = getRestTemplate();
            template.setMessageConverters(Arrays.asList(new ExcelConverter()));
    
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Arrays.asList(new MediaType("application")));
    
            HttpEntity<String> entity = new HttpEntity<String>(headers);
    
            ResponseEntity<byte[]> response = template.exchange(
                    url,
                    HttpMethod.GET, entity, byte[].class, "1");
    
            if (response.getStatusCode() == HttpStatus.OK) {
                Files.write(Paths.get(outputPath), response.getBody());
            }
        }
    }