Skip to content

Commit fb1f265

Browse files
author
Max Burnette
committed
download selected files from within a dataset
For code simplicity, still includes bagit support, dataset metadata, etc.
1 parent 6215e06 commit fb1f265

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

app/api/Datasets.scala

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,7 +2068,9 @@ class Datasets @Inject()(
20682068
* @return Enumerator to produce array of bytes from a zipped stream containing the bytes of each file
20692069
* in the dataset
20702070
*/
2071-
def enumeratorFromDataset(dataset: Dataset, chunkSize: Int = 1024 * 8, compression: Int = Deflater.DEFAULT_COMPRESSION, bagit: Boolean, user : Option[User])
2071+
def enumeratorFromDataset(dataset: Dataset, chunkSize: Int = 1024 * 8,
2072+
compression: Int = Deflater.DEFAULT_COMPRESSION, bagit: Boolean,
2073+
user : Option[User], fileIDs: Option[List[UUID]])
20722074
(implicit ec: ExecutionContext): Enumerator[Array[Byte]] = {
20732075
implicit val pec = ec.prepare()
20742076
val dataFolder = if (bagit) "data/" else ""
@@ -2077,7 +2079,17 @@ class Datasets @Inject()(
20772079

20782080
// compute list of all files and folder in dataset. This will also make sure
20792081
// that all files and folder names are unique.
2080-
listFilesInFolder(dataset.files, dataset.folders, dataFolder, filenameMap, inputFiles)
2082+
fileIDs match {
2083+
case Some(fids) => {
2084+
Logger.info("Downloading only some files")
2085+
Logger.info(fids.toString)
2086+
listFilesInFolder(fids, List.empty, dataFolder, filenameMap, inputFiles)
2087+
}
2088+
case None => {
2089+
// TODO: folderId match ...
2090+
listFilesInFolder(dataset.files, dataset.folders, dataFolder, filenameMap, inputFiles)
2091+
}
2092+
}
20812093

20822094
val md5Files = scala.collection.mutable.HashMap.empty[String, MessageDigest] //for the files
20832095
val md5Bag = scala.collection.mutable.HashMap.empty[String, MessageDigest] //for the bag files
@@ -2121,14 +2133,13 @@ class Datasets @Inject()(
21212133
* the enumerator is finished
21222134
*/
21232135

2124-
var is: Option[InputStream] = addDatasetInfoToZip(dataFolder,dataset,zip)
2136+
var is: Option[InputStream] = addDatasetInfoToZip(dataFolder, dataset, zip)
21252137
//digest input stream
21262138
val md5 = MessageDigest.getInstance("MD5")
21272139
md5Files.put(dataFolder+"_info.json",md5)
21282140
is = Some(new DigestInputStream(is.get,md5))
21292141
file_type = 1 //next is metadata
21302142

2131-
21322143
Enumerator.generateM({
21332144
is match {
21342145
case Some(inputStream) => {
@@ -2415,7 +2426,7 @@ class Datasets @Inject()(
24152426

24162427
// Use custom enumerator to create the zip file on the fly
24172428
// Use a 1MB in memory byte array
2418-
Ok.chunked(enumeratorFromDataset(dataset,1024*1024, compression,bagit,user)).withHeaders(
2429+
Ok.chunked(enumeratorFromDataset(dataset,1024*1024, compression, bagit, user, None)).withHeaders(
24192430
CONTENT_TYPE -> "application/zip",
24202431
CONTENT_DISPOSITION -> (FileUtils.encodeAttachment(dataset.name+ ".zip", request.headers.get("user-agent").getOrElse("")))
24212432
)
@@ -2427,6 +2438,30 @@ class Datasets @Inject()(
24272438
}
24282439
}
24292440

2441+
def downloadPartial(id: UUID, fileList: String) = PermissionAction(Permission.DownloadFiles, Some(ResourceRef(ResourceRef.dataset, id))) { implicit request =>
2442+
implicit val user = request.user
2443+
datasets.get(id) match {
2444+
case Some(dataset) => {
2445+
val fileIDs = fileList.split(',').map(fid => new UUID(fid)).toList
2446+
val bagit = play.api.Play.configuration.getBoolean("downloadDatasetBagit").getOrElse(true)
2447+
2448+
// Increment download count for each file
2449+
fileIDs.foreach(fid => files.incrementDownloads(fid, user))
2450+
2451+
// Use custom enumerator to create the zip file on the fly
2452+
// Use a 1MB in memory byte array
2453+
Ok.chunked(enumeratorFromDataset(dataset,1024*1024, -1, bagit, user, Some(fileIDs))).withHeaders(
2454+
CONTENT_TYPE -> "application/zip",
2455+
CONTENT_DISPOSITION -> (FileUtils.encodeAttachment(dataset.name+ " (Partial).zip", request.headers.get("user-agent").getOrElse("")))
2456+
)
2457+
}
2458+
// If the dataset wasn't found by ID
2459+
case None => {
2460+
NotFound
2461+
}
2462+
}
2463+
}
2464+
24302465
def updateAccess(id:UUID, access:String) = PermissionAction(Permission.PublicDataset, Some(ResourceRef(ResourceRef.dataset, id))) { implicit request =>
24312466
implicit val user = request.user
24322467
user match {

app/controllers/Application.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ class Application @Inject() (files: FileService, collections: CollectionService,
333333
api.routes.javascript.Datasets.unfollow,
334334
api.routes.javascript.Datasets.detachFile,
335335
api.routes.javascript.Datasets.download,
336+
api.routes.javascript.Datasets.downloadPartial,
336337
api.routes.javascript.Datasets.getPreviews,
337338
api.routes.javascript.Datasets.updateAccess,
338339
api.routes.javascript.Datasets.addFileEvent,

app/views/datasets/filesAndFolders.scala.html

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
//'<li><a onclick="showOnlyMarked();"><span class="glyphicon glyphicon-ok"/>&nbsp;Show Only Marked</a></li>' +
9090
//'<li><a onclick="showAllFiles();"><span class="glyphicon glyphicon-ok"/>&nbsp;Show All Files</a></li>' +
9191
// TODO: Support for downloading by folder and marked list will be implemented in the future
92-
//'<li><a onclick="downloadMarked();"><span class="glyphicon glyphicon-download-alt"/>&nbsp;Download All</a></li>' +
92+
'<li><a onclick="downloadMarked();"><span class="glyphicon glyphicon-download-alt"/>&nbsp;Download All</a></li>' +
9393
'<li><a onclick="confirmTagMarked();"><span class="glyphicon glyphicon-tag"/>&nbsp;Tag All</a></li>' +
9494
'<li><a onclick="confirmDeleteMarked();"><span class="glyphicon glyphicon-trash"/>&nbsp;Delete All</a></li>' +
9595
'<li><a onclick="clearMarked();"><span class="glyphicon glyphicon-erase"/>&nbsp;Clear All</a></li></ul>' +
@@ -160,12 +160,17 @@
160160
}
161161

162162
function showOnlyMarked() {
163-
confirmDeleteMarked('@dataset.id');
163+
console.log("Not implemented.")
164164
}
165165

166+
// Download selected files as Zip
166167
function downloadMarked() {
167-
// TODO: Downloading selected files as Zip - look at downloading folders tasks
168-
console.log("Not implemented.")
168+
var selected = $.cookie('[email protected]');
169+
if (selected) {
170+
window.open(jsRoutes.api.Datasets.downloadPartial('@dataset.id', selected).url, '_blank');
171+
} else {
172+
notify("No files selected.")
173+
}
169174
}
170175

171176
function confirmTagMarked() {

conf/routes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ GET /api/datasets/:id/files
574574
POST /api/datasets/:id/files @api.Datasets.uploadToDatasetFile(id: UUID)
575575
POST /api/datasets/:id/urls @api.Datasets.uploadToDatasetJSON(id: UUID)
576576
GET /api/datasets/:id/download @api.Datasets.download(id: UUID, compression: Int ?= -1, tracking: Boolean ?= true)
577+
GET /api/datasets/:id/downloadPartial @api.Datasets.downloadPartial(id: UUID, fileList: String)
577578
POST /api/datasets/:id/comment @api.Datasets.comment(id: UUID)
578579
POST /api/datasets/:id/reindex @api.Datasets.reindex(id:UUID, recursive: Boolean ?= true)
579580
POST /api/datasets/:id/follow @api.Datasets.follow(id: UUID)

0 commit comments

Comments
 (0)