Download all files from a document as ZIP

This commit is contained in:
jendib 2014-02-23 14:09:41 +01:00
parent ae566018d6
commit 34e3ac5478
6 changed files with 121 additions and 3 deletions

View File

@ -56,4 +56,29 @@ public class MimeTypeUtil {
return null;
}
/**
* Get a file extension linked to a MIME type.
*
* @param mimeType MIME type
* @return File extension
*/
public static String getFileExtension(String mimeType) {
switch (mimeType) {
case MimeType.APPLICATION_ZIP:
return "zip";
case MimeType.IMAGE_GIF:
return "gif";
case MimeType.IMAGE_JPEG:
return "jpg";
case MimeType.IMAGE_PNG:
return "png";
case MimeType.IMAGE_X_ICON:
return "ico";
case MimeType.APPLICATION_PDF:
return "pdf";
default:
return null;
}
}
}

View File

@ -1,3 +1,2 @@
- Automatic backup system using Quartz (server)
- Handle error while uploading a file
- Download all files as zip (docs & share)

View File

@ -11,6 +11,8 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.persistence.NoResultException;
import javax.ws.rs.Consumes;
@ -372,4 +374,75 @@ public class FileResource extends BaseResource {
.header("Expires", new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date().getTime() + 3600000 * 24))
.build();
}
/**
* Returns all files from a document, zipped.
*
* @param documentId Document ID
* @return Response
* @throws JSONException
*/
@GET
@Path("zip")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response zip(
@QueryParam("id") String documentId,
@QueryParam("share") String shareId) throws JSONException {
authenticate();
// Get the document
DocumentDao documentDao = new DocumentDao();
Document document;
try {
document = documentDao.getDocument(documentId);
// Check document visibility
ShareDao shareDao = new ShareDao();
if (!shareDao.checkVisibility(document, principal.getId(), shareId)) {
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
}
// Get files and user associated with this document
FileDao fileDao = new FileDao();
UserDao userDao = new UserDao();
final List<File> fileList = fileDao.getByDocumentId(documentId);
final User user = userDao.getById(document.getUserId());
// Create the ZIP stream
StreamingOutput stream = new StreamingOutput() {
@Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
// Add each file to the ZIP stream
int index = 0;
for (File file : fileList) {
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
InputStream fileInputStream = new FileInputStream(storedfile);
// Add the decrypted file to the ZIP stream
try (InputStream decryptedStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey())) {
ZipEntry zipEntry = new ZipEntry(index + "." + MimeTypeUtil.getFileExtension(file.getMimeType()));
zipOutputStream.putNextEntry(zipEntry);
ByteStreams.copy(decryptedStream, zipOutputStream);
zipOutputStream.closeEntry();
} catch (Exception e) {
e.printStackTrace();
throw new WebApplicationException(e);
}
index++;
}
}
outputStream.close();
}
};
// Write to the output
return Response.ok(stream)
.header("Content-Type", "application/zip")
.header("Content-Disposition", "attachment; filename=\"" + document.getTitle().replaceAll("\\W+", "_") + ".zip\"")
.build();
}
}

View File

@ -9,7 +9,13 @@
</div>
<div class="page-header">
<h1>{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small> <img ng-if="document" ng-src="img/flag/{{ document.language }}.png" title="{{ document.language }}" /></h1>
<h1>
{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small>
<img ng-if="document" ng-src="img/flag/{{ document.language }}.png" title="{{ document.language }}" />
<a ng-href="../api/file/zip?id={{ document.id }}" class="btn btn-default" title="Download all files">
<span class="glyphicon glyphicon-compressed"></span>
</a>
</h1>
<p>
<button class="btn btn-sm btn-info" ng-click="share()">
<span class="glyphicon glyphicon-share"></span> Share

View File

@ -1,7 +1,12 @@
<div class="row">
<div class="well col-md-12">
<div class="page-header">
<h1>{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small></h1>
<h1>
{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small>
<a ng-href="../api/file/zip?id={{ document.id }}&share={{ $stateParams.shareId }}" class="btn btn-default" title="Download all files">
<span class="glyphicon glyphicon-compressed"></span>
</a>
</h1>
<ul class="list-inline">
<li ng-repeat="tag in document.tags"><span class="label label-info" ng-style="{ 'background': tag.color }">{{ tag.name }}</span></li>
</ul>

View File

@ -160,6 +160,16 @@ public class TestFileResource extends BaseJerseyTest {
Assert.assertEquals(file2Id, files.getJSONObject(0).getString("id"));
Assert.assertEquals(file1Id, files.getJSONObject(1).getString("id"));
// Get a ZIP from all files
fileResource = resource().path("/file/zip");
fileResource.addFilter(new CookieAuthenticationFilter(file1AuthenticationToken));
getParams = new MultivaluedMapImpl();
getParams.putSingle("id", document1Id);
response = fileResource.queryParams(getParams).get(ClientResponse.class);
is = response.getEntityInputStream();
fileBytes = ByteStreams.toByteArray(is);
Assert.assertEquals(MimeType.APPLICATION_ZIP, MimeTypeUtil.guessMimeType(fileBytes));
// Deletes a file
fileResource = resource().path("/file/" + file1Id);
fileResource.addFilter(new CookieAuthenticationFilter(file1AuthenticationToken));