Skip to content

Commit

Permalink
Ufal 325/be preview files from zip (#388)
Browse files Browse the repository at this point in the history
* add the controller, file for handling the logic, wrapper and testing

* add Id for the object response

* add controller for handling download feature

* extract zip each time the preview section call api

* handle the restriction for download

* add redirect feature and handle large files upload

* add license header for new files

* format style code

* change wrong spelling

* add license header

---------

Co-authored-by: HuynhKhoa1601 <huynhnhatkhoa21@gmail.com>
  • Loading branch information
milanmajchrak and HuynhKhoa1601 authored Aug 4, 2023
1 parent ab039e5 commit 635d74a
Show file tree
Hide file tree
Showing 10 changed files with 1,303 additions and 0 deletions.
40 changes: 40 additions & 0 deletions dspace-api/src/main/java/org/dspace/util/FileInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.util;

import java.util.Hashtable;
/**
* This class is used to store the information about a file or a directory
*
* @author longtv
*/
public class FileInfo {

public String name;
public String content;
public String size;
public boolean isDirectory;

public Hashtable<String, FileInfo> sub = null;

public FileInfo(String name) {
this.name = name;
sub = new Hashtable<String, FileInfo>();
isDirectory = true;
}
public FileInfo(String content, boolean isDirectory) {
this.content = content;
this.isDirectory = isDirectory;
}

public FileInfo(String name, String size) {
this.name = name;
this.size = size;
isDirectory = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.util;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Generate a tree view of the file in a bitstream
*
* @author longtv
*/
public class FileTreeViewGenerator {
private FileTreeViewGenerator () {
}

public static List<FileInfo> parse(String data) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(data)));
Element rootElement = document.getDocumentElement();
NodeList nl = rootElement.getChildNodes();
FileInfo root = new FileInfo("root");
int limitFile = 100;
int countFile = 0;
Node n = nl.item(0);
do {
String fileInfo = n.getFirstChild().getTextContent();
String f[] = fileInfo.split("\\|");
String fileName = "";
String path = f[0];
long size = Long.parseLong(f[1]);
if (!path.endsWith("/")) {
fileName = path.substring(path.lastIndexOf('/') + 1);
if (path.lastIndexOf('/') != -1) {
path = path.substring(0, path.lastIndexOf('/'));
} else {
path = "";
}
}
FileInfo current = root;
for (String p : path.split("/")) {
if (current.sub.containsKey(p)) {
current = current.sub.get(p);
} else {
FileInfo temp = new FileInfo(p);
current.sub.put(p, temp);
current = temp;
}
}
if (!fileName.isEmpty()) {
FileInfo temp = new FileInfo(fileName, humanReadableFileSize(size));
current.sub.put(fileName, temp);
countFile++;
}
} while ((n = n.getNextSibling()) != null && countFile < limitFile);
return new ArrayList<>(root.sub.values());
}
public static String humanReadableFileSize(long bytes) {
int thresh = 1024;
if (Math.abs(bytes) < thresh) {
return bytes + " B";
}
String units[] = {"kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
int u = -1;
do {
bytes /= thresh;
++u;
} while (Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes + " " + units[u];
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;


import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.model.MetadataBitstreamWrapper;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.MissingLicenseAgreementException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.*;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Context;
import org.dspace.handle.service.HandleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.dspace.core.Constants;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.Deflater;
import java.util.zip.ZipOutputStream;

@RestController
@RequestMapping("/bitstream")
public class MetadataBitstreamController {

@Autowired
BitstreamService bitstreamService;

@Autowired
HandleService handleService;
@Autowired
private AuthorizeService authorizeService;
@GetMapping("/handle/{id}/{subId}/{fileName}")
public ResponseEntity<Resource> downloadSingleFile(
@PathVariable("id") String id,
@PathVariable("subId") String subId,
@PathVariable("fileName") String fileName,
HttpServletRequest request, HttpServletResponse response) throws IOException {
String handleID = id + "/" + subId;
if (StringUtils.isBlank(handleID)) {
throw new DSpaceBadRequestException("handle cannot be null!");
}
Context context = ContextUtil.obtainContext(request);
if (Objects.isNull(context)) {
throw new RuntimeException("Cannot obtain the context from the request.");
}

DSpaceObject dso = null;

try{
dso = handleService.resolveToObject(context, handleID);
} catch (Exception e) {
throw new RuntimeException("Cannot resolve handle: " + handleID);
}

if (dso != null && dso instanceof Item) {
Item item = (Item) dso;
List<Bundle> bundles = item.getBundles();
for (Bundle bundle:
bundles) {
for (Bitstream bitstream:
bundle.getBitstreams()) {
try {
authorizeService.authorizeAction(context, bitstream, Constants.READ);
} catch (MissingLicenseAgreementException e) {
response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download");
} catch (AuthorizeException e) {
response.sendRedirect("http://localhost:4000" + "/login");
} catch (SQLException e) {
response.sendRedirect("http://localhost:4000" + "/login");
}
String btName = bitstream.getName();
if (btName.equalsIgnoreCase(fileName)) {
try {
BitstreamFormat bitstreamFormat = bitstream.getFormat(context);
if (bitstreamFormat == null || bitstreamFormat.getExtensions() == null || bitstreamFormat.getExtensions().size() == 0) {
// throw new RuntimeException("Cannot find the bitstream format.");
}
InputStream inputStream = bitstreamService.retrieve(context, bitstream);
InputStreamResource resource = new InputStreamResource(inputStream);
HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName);
// "attachment; filename=" + fileName +".pdf");
header.add("Cache-Control", "no-cache, no-store, must-revalidate");
header.add("Pragma", "no-cache");
header.add("Expires", "0");
return ResponseEntity.ok()
.headers(header)
.contentLength(inputStream.available())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}

return null;
}


@GetMapping("/allzip")
public void downloadFileZip(@RequestParam("handleId") String handleId,
HttpServletResponse response,
HttpServletRequest request) throws IOException, SQLException, AuthorizeException {
//TODO handle authorization
if (StringUtils.isBlank(handleId)) {
throw new DSpaceBadRequestException("handle cannot be null!");
}
Context context = ContextUtil.obtainContext(request);
if (Objects.isNull(context)) {
throw new RuntimeException("Cannot obtain the context from the request.");
}

DSpaceObject dso = null;
String name = "";
try{
dso = handleService.resolveToObject(context, handleId);
} catch (Exception e) {
throw new RuntimeException("Cannot resolve handle: " + handleId);
}

if (dso != null && dso instanceof Item) {
Item item = (Item) dso;
name = item.getName() + ".zip";
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", name));
response.setContentType("application/zip");
List<Bundle> bundles = item.getBundles("ORIGINAL");


ZipArchiveOutputStream zip = new ZipArchiveOutputStream(response.getOutputStream());
zip.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS);
zip.setLevel(Deflater.NO_COMPRESSION);
for (Bundle original : bundles) {
List<Bitstream> bss = original.getBitstreams();
for (Bitstream bitstream : bss) {
try {
authorizeService.authorizeAction(context, bitstream, Constants.READ);
} catch (AuthorizeException e) {
response.sendRedirect("http://localhost:4000" + "/login");
} catch (SQLException e) {
response.sendRedirect("http://localhost:4000" + "/login");
}
String filename = bitstream.getName();
ZipArchiveEntry ze = new ZipArchiveEntry(filename);
zip.putArchiveEntry(ze);
InputStream is = bitstreamService.retrieve(context, bitstream);
IOUtils.copy(is, zip);
zip.closeArchiveEntry();
is.close();
}
}
zip.close();
response.getOutputStream().flush();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.converter;

import org.dspace.app.rest.model.MetadataBitstreamWrapper;
import org.dspace.app.rest.model.MetadataBitstreamWrapperRest;
import org.dspace.app.rest.model.MetadataValueWrapper;
import org.dspace.app.rest.model.MetadataValueWrapperRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.core.Context;
import org.dspace.util.FileTreeViewGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
public class MetadataBitstreamWrapperConverter implements DSpaceConverter<MetadataBitstreamWrapper, MetadataBitstreamWrapperRest> {

@Lazy
@Autowired
private ConverterService converter;


@Autowired
private BitstreamConverter bitstreamConverter;

@Override
public MetadataBitstreamWrapperRest convert(MetadataBitstreamWrapper modelObject, Projection projection) {
MetadataBitstreamWrapperRest bitstreamWrapperRest = new MetadataBitstreamWrapperRest();
bitstreamWrapperRest.setProjection(projection);
bitstreamWrapperRest.setName(modelObject.getBitstream().getName());
bitstreamWrapperRest.setId(modelObject.getBitstream().getID().toString());
bitstreamWrapperRest.setDescription(modelObject.getDescription());
bitstreamWrapperRest.setChecksum(modelObject.getBitstream().getChecksum());
bitstreamWrapperRest.setFileSize(FileTreeViewGenerator.humanReadableFileSize(modelObject.getBitstream().getSizeBytes()));
bitstreamWrapperRest.setFileInfo(modelObject.getFileInfo());
bitstreamWrapperRest.setHref(modelObject.getHref());
bitstreamWrapperRest.setFormat(modelObject.getFormat());
bitstreamWrapperRest.setCanPreview(modelObject.isCanPreview());
return bitstreamWrapperRest;
}

@Override
public Class<MetadataBitstreamWrapper> getModelClass() {
return MetadataBitstreamWrapper.class;
}
}
Loading

0 comments on commit 635d74a

Please sign in to comment.