diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index 0e2e338404d..8a809e679d5 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -67,6 +67,8 @@ Multiple File ("bundle") download ``/api/access/datafiles/$id1,$id2,...$idN`` +Alternate Form: POST to ``/api/access/datafiles`` with a ``fileIds`` input field containing the same comma separated list of file ids. This is most useful when your list of files surpasses the allowed URL length (varies but can be ~2000 characters). + Returns the files listed, zipped. .. note:: If the request can only be completed partially - if only *some* of the requested files can be served (because of the permissions and/or size restrictions), the file MANIFEST.TXT included in the zipped bundle will have entries specifying the reasons the missing files could not be downloaded. IN THE FUTURE the API will return a 207 status code to indicate that the result was a partial success. (As of writing this - v.4.11 - this hasn't been implemented yet) diff --git a/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java index 8e9695e086d..0832ec1db79 100644 --- a/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java @@ -182,6 +182,7 @@ public void writeGuestbookResponseRecord(GuestbookResponse guestbookResponse) { mdcLogService.logEntry(entry); } catch (CommandException e) { //if an error occurs here then download won't happen no need for response recs... + logger.warning("Exception writing GuestbookResponse for file: " + guestbookResponse.getDataFile().getId() + " : " + e.getLocalizedMessage()); } } @@ -203,7 +204,7 @@ public void writeGuestbookResponseRecord(GuestbookResponse guestbookResponse) { // to the API. private void redirectToBatchDownloadAPI(String multiFileString, Boolean guestbookRecordsAlreadyWritten, Boolean downloadOriginal){ - String fileDownloadUrl = "/api/access/datafiles/" + multiFileString; + String fileDownloadUrl = "/api/access/datafiles"; if (guestbookRecordsAlreadyWritten && !downloadOriginal){ fileDownloadUrl += "?gbrecs=true"; } else if (guestbookRecordsAlreadyWritten && downloadOriginal){ @@ -212,11 +213,7 @@ private void redirectToBatchDownloadAPI(String multiFileString, Boolean guestboo fileDownloadUrl += "?format=original"; } - try { - FacesContext.getCurrentInstance().getExternalContext().redirect(fileDownloadUrl); - } catch (IOException ex) { - logger.info("Failed to issue a redirect to file download url."); - } + PrimeFaces.current().executeScript("downloadFiles('"+fileDownloadUrl + "','"+ multiFileString+"');"); } diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Access.java b/src/main/java/edu/harvard/iq/dataverse/api/Access.java index 42328367241..ea2d512f98f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Access.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Access.java @@ -103,9 +103,11 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.ws.rs.BadRequestException; +import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.ForbiddenException; import javax.ws.rs.NotFoundException; +import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.QueryParam; import javax.ws.rs.ServiceUnavailableException; @@ -525,15 +527,34 @@ public DownloadInstance tabularDatafileMetadataPreprocessed(@PathParam("fileId") } /* - * API method for downloading zipped bundles of multiple files: + * API method for downloading zipped bundles of multiple files. Uses POST to avoid long lists of file IDs that can make the URL longer than what's supported by browsers/servers */ - // TODO: Rather than only supporting looking up files by their database IDs, consider supporting persistent identifiers. + // TODO: Rather than only supporting looking up files by their database IDs, + // consider supporting persistent identifiers. + @Path("datafiles") + @POST + @Consumes("text/plain") + @Produces({ "application/zip" }) + public Response postDownloadDatafiles(String fileIds, @QueryParam("gbrecs") boolean gbrecs, @QueryParam("key") String apiTokenParam, @Context UriInfo uriInfo, @Context HttpHeaders headers, @Context HttpServletResponse response) throws WebApplicationException { + + + return downloadDatafiles(fileIds, gbrecs, apiTokenParam, uriInfo, headers, response); + } + /* + * API method for downloading zipped bundles of multiple files: + */ + + // TODO: Rather than only supporting looking up files by their database IDs, + // consider supporting persistent identifiers. @Path("datafiles/{fileIds}") @GET @Produces({"application/zip"}) - public Response datafiles(@PathParam("fileIds") String fileIds, @QueryParam("gbrecs") boolean gbrecs, @QueryParam("key") String apiTokenParam, @Context UriInfo uriInfo, @Context HttpHeaders headers, @Context HttpServletResponse response) throws WebApplicationException /*throws NotFoundException, ServiceUnavailableException, PermissionDeniedException, AuthorizationRequiredException*/ { + public Response datafiles(@PathParam("fileIds") String fileIds, @QueryParam("gbrecs") boolean gbrecs, @QueryParam("key") String apiTokenParam, @Context UriInfo uriInfo, @Context HttpHeaders headers, @Context HttpServletResponse response) throws WebApplicationException { + return downloadDatafiles(fileIds, gbrecs, apiTokenParam, uriInfo, headers, response); + } + private Response downloadDatafiles(String rawFileIds, boolean gbrecs, String apiTokenParam, UriInfo uriInfo, HttpHeaders headers, HttpServletResponse response) throws WebApplicationException /* throws NotFoundException, ServiceUnavailableException, PermissionDeniedException, AuthorizationRequiredException*/ { long setLimit = systemConfig.getZipDownloadLimit(); if (!(setLimit > 0L)) { setLimit = DataFileZipper.DEFAULT_ZIPFILE_LIMIT; @@ -543,10 +564,19 @@ public Response datafiles(@PathParam("fileIds") String fileIds, @QueryParam("gb logger.fine("setting zip download size limit to " + zipDownloadSizeLimit + " bytes."); - if (fileIds == null || fileIds.equals("")) { + if (rawFileIds == null || rawFileIds.equals("")) { throw new BadRequestException(); } - + final String fileIds; + if(rawFileIds.startsWith("fileIds=")) { + fileIds = rawFileIds.substring(8); // String "fileIds=" from the front + } else { + fileIds=rawFileIds; + } + /* Note - fileIds coming from the POST ends in '\n' and a ',' has been added after the last file id number and before a + * final '\n' - this stops the last item from being parsed in the fileIds.split(","); line below. + */ + String apiToken = (apiTokenParam == null || apiTokenParam.equals("")) ? headers.getHeaderString(API_KEY_HEADER) : apiTokenParam; diff --git a/src/main/webapp/file-download-button-fragment.xhtml b/src/main/webapp/file-download-button-fragment.xhtml index da5d90a572a..683ea10d45e 100644 --- a/src/main/webapp/file-download-button-fragment.xhtml +++ b/src/main/webapp/file-download-button-fragment.xhtml @@ -277,4 +277,13 @@ #{fileMetadata.dataFile.fileAccessRequesters.contains(dataverseSession.user) ? bundle['file.accessRequested'] : bundle['file.requestAccess']} +