From 21ee3973536d05038b5bc0494eede3a4f4f16c01 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Wed, 12 Jul 2023 18:48:40 +0700 Subject: [PATCH 01/10] add the controller, file for handling the logic, wrapper and testing --- .../main/java/org/dspace/util/FileInfo.java | 33 +++ .../dspace/util/FileTreeViewGenerator.java | 83 ++++++ .../MetadataBitstreamWrapperConverter.java | 44 +++ .../model/MetadataBitstreamWrapperRest.java | 116 ++++++++ .../MetadataBitstreamWrapperResource.java | 12 + .../wrapper/MetadataBitstreamWrapper.java | 67 +++++ .../MetadataBitstreamRestRepository.java | 266 ++++++++++++++++++ .../MetadataBitstreamRestRepositoryIT.java | 185 ++++++++++++ 8 files changed, 806 insertions(+) create mode 100644 dspace-api/src/main/java/org/dspace/util/FileInfo.java create mode 100644 dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java diff --git a/dspace-api/src/main/java/org/dspace/util/FileInfo.java b/dspace-api/src/main/java/org/dspace/util/FileInfo.java new file mode 100644 index 000000000000..2408430700e4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/util/FileInfo.java @@ -0,0 +1,33 @@ +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 sub = null; + + public FileInfo(String name) { + this.name = name; + sub = new Hashtable(); + 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; + } +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java new file mode 100644 index 000000000000..db302310aac5 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java @@ -0,0 +1,83 @@ +package org.dspace.util; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +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; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +public class FileTreeViewGenerator { + + public static List 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"); + 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); + } + + } while((n=n.getNextSibling())!=null); + + 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]; + } + +} + diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java new file mode 100644 index 000000000000..342f68bfd7a4 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -0,0 +1,44 @@ +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 { + + @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.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 getModelClass() { + return MetadataBitstreamWrapper.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java new file mode 100644 index 000000000000..e50f19c5407b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java @@ -0,0 +1,116 @@ +package org.dspace.app.rest.model; + +import org.dspace.app.rest.RestResourceController; +import org.dspace.util.FileInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class MetadataBitstreamWrapperRest extends BaseObjectRest{ + public static final String NAME = "metadatabitstream"; + public static final String CATEGORY = RestAddressableModel.CORE; + + private String name; + private String description; + private String fileSize; + private String checksum; + private List fileInfo; + private String format; + private String href; + private boolean canPreview; + + public MetadataBitstreamWrapperRest(String name, String description, String fileSize, String checksum, List fileInfo, String format, String href, boolean canPreview) { + this.name = name; + this.description = description; + this.fileSize = fileSize; + this.checksum = checksum; + this.fileInfo = fileInfo; + this.format = format; + this.href = href; + this.canPreview = canPreview; + } + + public void setCanPreview(boolean canPreview) { + this.canPreview = canPreview; + } + + public boolean isCanPreview() { + return canPreview; + } + + public MetadataBitstreamWrapperRest() { + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public List getFileInfo() { + return fileInfo; + } + + public void setFileInfo(List fileInfo) { + this.fileInfo = fileInfo; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getFileSize() { + return fileSize; + } + + public void setFileSize(String fileSize) { + this.fileSize = fileSize; + } + + public String getChecksum() { + return checksum; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java new file mode 100644 index 000000000000..3d0aa7335f5b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java @@ -0,0 +1,12 @@ +package org.dspace.app.rest.model.hateoas; + +import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +@RelNameDSpaceResource(MetadataBitstreamWrapperRest.NAME) +public class MetadataBitstreamWrapperResource extends DSpaceResource{ + public MetadataBitstreamWrapperResource(MetadataBitstreamWrapperRest data, Utils utils) { + super(data, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java new file mode 100644 index 000000000000..77e66e8ecb60 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java @@ -0,0 +1,67 @@ +package org.dspace.app.rest.model; + +import org.dspace.content.Bitstream; +import org.dspace.util.FileInfo; + +import java.util.List; + +public class MetadataBitstreamWrapper { + private Bitstream bitstream; + private List fileInfo; + private String format; + private String description; + private String href; + + private boolean canPreview; + public MetadataBitstreamWrapper() { + } + + public MetadataBitstreamWrapper(Bitstream bitstream, List fileInfo, String format, String description, String href, boolean canPreview) { + this.bitstream = bitstream; + this.fileInfo = fileInfo; + this.format = format; + this.description = description; + this.href = href; + this.canPreview = canPreview; + } + + public String getDescription() { + return description; + } + + public boolean isCanPreview() { + return canPreview; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public Bitstream getBitstream() { + return bitstream; + } + + public void setBitstream(Bitstream bitstream) { + this.bitstream = bitstream; + } + + public List getFileInfo() { + return fileInfo; + } + + public void setFileInfo(List fileInfo) { + this.fileInfo = fileInfo; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java new file mode 100644 index 000000000000..4330d050909b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -0,0 +1,266 @@ +package org.dspace.app.rest.repository; + + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.Parameter; +import org.dspace.app.rest.SearchRestMethod; +import org.dspace.app.rest.converter.BitstreamConverter; +import org.dspace.app.rest.converter.MetadataBitstreamWrapperConverter; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.MetadataBitstreamWrapper; +import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; +import org.dspace.app.util.Util; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.*; +import org.dspace.content.clarin.ClarinLicense; +import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; +import org.dspace.core.Constants; +import org.dspace.core.Context; +import org.dspace.handle.service.HandleService; +import org.dspace.util.FileInfo; +import org.dspace.util.FileTreeViewGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; +import org.xml.sax.SAXException; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) +public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamRestRepository.class); + @Autowired + HandleService handleService; + + @Autowired + BitstreamConverter bitstreamConverter; + + @Autowired + MetadataBitstreamWrapperConverter metadataBitstreamWrapperConverter; + @Autowired + ItemService itemService; + @Autowired + ClarinLicenseResourceMappingService licenseService; + + @Autowired + AuthorizeService authorizeService; + + @SearchRestMethod(name = "byHandle") + public Page findByHandle(@Parameter(value = "handle", required = true) String handle, + @Parameter(value = "fileGrpType", required = false) String fileGrpType, + Pageable pageable) + throws SQLException, ParserConfigurationException, IOException, SAXException { + if (StringUtils.isBlank(handle)) { + throw new DSpaceBadRequestException("handle cannot be null!"); + } + Context context = obtainContext(); + if (Objects.isNull(context)) { + throw new RuntimeException("Cannot obtain the context from the request."); + } + HttpServletRequest request = getRequestService().getCurrentRequest().getHttpServletRequest(); + String contextPath = request.getContextPath(); + List rs = new ArrayList<>(); + DSpaceObject dso = null; + + try{ + dso = handleService.resolveToObject(context, handle); + } catch (Exception e) { + throw new RuntimeException("Cannot resolve handle: " + handle); + } + + if ( dso instanceof Item) { + Item item = (Item) dso; + List fileGrpTypes = Arrays.asList(fileGrpType.split(",")); + List bundles = findEnabledBundles(fileGrpTypes, item); + + for (Bundle bundle : + bundles) { + List bitstreams = new ArrayList<>(); + String use = bundle.getName(); + if ("THUMBNAIL".equals(use)) + { + Thumbnail thumbnail = itemService.getThumbnail(context, item, false); + if(thumbnail != null) { + bitstreams.add(thumbnail.getThumb()); + } + } + else + { + bitstreams = bundle.getBitstreams(); + } + + for (Bitstream bitstream : + bitstreams) { + List clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); + boolean canPreview = false; + if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { + ClarinLicenseResourceMapping licenseResourceMapping = clarinLicenseResourceMappings.get(0); + ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); + canPreview = clarinLicense.getClarinLicenseLabels().stream().anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); + } + String identifier = null; + if (item != null && item.getHandle() != null) + { + identifier = "handle/" + item.getHandle(); + } + else if (item != null) + { + identifier = "item/" + item.getID(); + } + else + { + identifier = "id/" + bitstream.getID(); + } + String url = contextPath + "/bitstream/"+identifier+"/"; + try + { + if (bitstream.getName() != null) + { + url += Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); + } + } + catch (UnsupportedEncodingException uee) + { + log.error("UnsupportedEncodingException", uee); + } + + url += "?sequence="+bitstream.getSequenceID(); + + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + + url += "&isAllowed=" + isAllowed; + if (true) { + List metadataValues = bitstream.getMetadata(); + // Filter out all metadata values that are not local to the bitstream + // Uncomment this if we want to show metadata values that are local to the bitstream +// metadataValues = metadataValues.stream().filter(metadataValue -> +// match("local", "bitstream", "file", metadataValue.getMetadataField())) +// .collect(Collectors.toList()); + List fileInfos = new ArrayList<>(); + if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { + List finalFileInfos = fileInfos; + metadataValues.stream().map(MetadataValue::getValue).reduce((s, s2) -> s + s2) + .ifPresent(s -> finalFileInfos.add(new FileInfo(s, false))); + fileInfos = finalFileInfos; + } else { + StringBuilder sb = new StringBuilder(); + sb.append(""); + for (MetadataValue metadataValue : + metadataValues) { + sb.append(""); + sb.append(metadataValue.getValue()); + sb.append(""); + } + sb.append(""); + try { + fileInfos = FileTreeViewGenerator.parse(sb.toString()); + } catch (Exception e) { + fileInfos = null; + } + } + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); + } else { + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, null, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); + continue; + } + } + } + } + + return new PageImpl<>(rs, pageable, rs.size()); + } + + protected List findEnabledBundles(List fileGrpTypes, Item item) throws SQLException + { + // Check if the user is requested a specific bundle or + // the all bundles. + List bundles; + if (fileGrpTypes.size() == 0) + { + bundles = item.getBundles(); + } + else + { + bundles = new ArrayList(); + for (String fileGrpType : fileGrpTypes) + { + for (Bundle newBundle : item.getBundles(fileGrpType)) + { + bundles.add(newBundle); + } + } + } + + return bundles; + } + + + private boolean match(String schema, String element, String qualifier, MetadataField field) + { + if (!element.equals(Item.ANY) && !element.equals(field.getElement())) + { + return false; + } + + if (qualifier == null) + { + if (field.getQualifier() != null) + { + return false; + } + } + else if (!qualifier.equals(Item.ANY)) + { + if (!qualifier.equals(field.getQualifier())) + { + return false; + } + } + + if (!schema.equals(Item.ANY)) + { + if (field.getMetadataSchema() != null && !field.getMetadataSchema().getName().equals(schema)) + { + return false; + } + } + return true; + } + + + @Override + public MetadataBitstreamWrapperRest findOne(Context context, Integer integer) { + return null; + } + + @Override + public Page findAll(Context context, Pageable pageable) { + return null; + } + + @Override + public Class getDomainClass() { + return MetadataBitstreamWrapperRest.class; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java new file mode 100644 index 000000000000..9ef31dc5c4ab --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -0,0 +1,185 @@ +package org.dspace.app.rest; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.app.util.Util; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.builder.*; +import org.dspace.content.*; +import org.dspace.content.clarin.ClarinLicense; +import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; +import org.dspace.core.Constants; +import org.dspace.util.FileTreeViewGenerator; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegrationTest { + + private static final String HANDLE_ID = "123456789/36"; + private static final String METADATABITSTREAM_ENDPOINT = "/api/core/metadatabitstream/"; + private static final String METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "search/byHandle"; + private static final String FILE_GRP_TYPE = "ORIGINAL"; + private static final String AUTHOR = "Test author name"; + private Collection col; + + private Item publicItem; + private Bitstream bts; + private Bundle bundle; + private Boolean canPreview = false; + + @Autowired + ClarinLicenseResourceMappingService licenseService; + + @Autowired + AuthorizeService authorizeService; + + + @Test + public void findByHandleNullHandle() throws Exception { + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT)) + .andExpect(status().isBadRequest()); + } + + @Test + public void findByHandle() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build(); + + publicItem = ItemBuilder.createItem(context, col) + .withAuthor(AUTHOR) + .build(); + + String bitstreamContent = "ThisIsSomeDummyText"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bts = BitstreamBuilder. + createBitstream(context, publicItem, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("application/x-gzip") + .build(); + } + + String identifier = null; + if (publicItem != null && publicItem.getHandle() != null) + { + identifier = "handle/" + publicItem.getHandle(); + } + else if (publicItem != null) + { + identifier = "item/" + publicItem.getID(); + } + else + { + identifier = "id/" + bts.getID(); + } + String url = "/bitstream/"+identifier+"/"; + try + { + if (bts.getName() != null) + { + url += Util.encodeBitstreamName(bts.getName(), "UTF-8"); + } + } + catch (UnsupportedEncodingException uee) + { + + } + + url += "?sequence=" + bts.getSequenceID(); + + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bts, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + + url += "&isAllowed=" + isAllowed; + + context.restoreAuthSystemState(); + List bundles = publicItem.getBundles(FILE_GRP_TYPE); + for (Bundle bundle : bundles) { + bundle.getBitstreams().stream().forEach(bitstream -> { + List clarinLicenseResourceMappings = null; + try { + clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { + ClarinLicenseResourceMapping licenseResourceMapping = clarinLicenseResourceMappings.get(0); + ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); + canPreview = clarinLicense.getClarinLicenseLabels().stream() + .anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); + } + }); + } + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) + .param("handle", publicItem.getHandle()) + .param("fileGrpType", FILE_GRP_TYPE)) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.metadatabitstreams").exists()) + .andExpect(jsonPath("$._embedded.metadatabitstreams").isArray()) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].name") + .value(Matchers.containsInAnyOrder(Matchers.containsString("Bitstream")))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].description") + .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getFormatDescription(context))))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].format") + .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getFormat(context).getMIMEType())))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].fileSize") + .value(Matchers.containsInAnyOrder(Matchers.containsString(FileTreeViewGenerator.humanReadableFileSize(bts.getSizeBytes()))))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].canPreview") + .value(Matchers.containsInAnyOrder(Matchers.is(canPreview)))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].fileInfo").exists()) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].checksum") + .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getChecksum())))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].href") + .value(Matchers.containsInAnyOrder(Matchers.containsString(url)))); + + + } + + @Test + public void findByHandleEmptyFileGrpType() throws Exception { + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) + .param("handle", HANDLE_ID) + .param("fileGrpType", "")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.page.totalElements", is(0))) + .andExpect(jsonPath("$.page.totalPages", is(0))) + .andExpect(jsonPath("$.page.size", is(20))) + .andExpect(jsonPath("$.page.number", is(0))) + .andExpect(jsonPath("$._links.self.href", Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + "?handle=" + HANDLE_ID + "&fileGrpType="))); + } + + @Test + public void searchMethodsExist() throws Exception { + + getClient().perform(get("/api/core/metadatabitstreams")) + .andExpect(status().is5xxServerError()); + + getClient().perform(get("/api/core/metadatabitstreams/search")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.byHandle", notNullValue())); + } +} From bfaf2aa1dc4461363ea62a327ce24c891b62abda Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Fri, 14 Jul 2023 19:43:51 +0700 Subject: [PATCH 02/10] add Id for the object response --- .../app/rest/converter/MetadataBitstreamWrapperConverter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java index 342f68bfd7a4..dd6ab981288f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -27,6 +27,7 @@ public MetadataBitstreamWrapperRest convert(MetadataBitstreamWrapper modelObject 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())); From ceae3d6bf93867f3603a7dd9594c23d993135f64 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Wed, 19 Jul 2023 19:21:28 +0700 Subject: [PATCH 03/10] add controller for handling download feature --- .../app/rest/MetadataBitstreamController.java | 161 ++++++++++++++++++ .../MetadataBitstreamRestRepository.java | 11 ++ 2 files changed, 172 insertions(+) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java new file mode 100644 index 000000000000..86518f37092c --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -0,0 +1,161 @@ +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.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 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; + + @GetMapping("/handle/{id}/{subId}/{fileName}") + public ResponseEntity 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 bundles = item.getBundles(); + for (Bundle bundle: + bundles) { + for (Bitstream bitstream: + bundle.getBitstreams()) { + 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 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 bss = original.getBitstreams(); + for (Bitstream bitstream : bss) { + 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(); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 4330d050909b..ed5ef85ccc3d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -11,6 +11,7 @@ import org.dspace.app.rest.model.MetadataBitstreamWrapper; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; import org.dspace.app.util.Util; +import org.dspace.content.service.BitstreamService; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.*; import org.dspace.content.clarin.ClarinLicense; @@ -32,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.sql.SQLException; import java.util.ArrayList; @@ -56,6 +58,9 @@ public class MetadataBitstreamRestRepository extends DSpaceRestRepository findByHandle(@Parameter(value = "handl for (Bitstream bitstream : bitstreams) { +// try { +// InputStream inputStream = bitstreamService.retrieve(context, bitstream); +// log.error("inputStream: " + inputStream); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } List clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); boolean canPreview = false; if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { From fcb5bbc047796ed1b72c98ce651ed6b5c7b443c2 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Fri, 21 Jul 2023 19:17:43 +0700 Subject: [PATCH 04/10] extract zip each time the preview section call api --- .../MetadataBitstreamRestRepository.java | 195 ++++++++++++------ 1 file changed, 128 insertions(+), 67 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index ed5ef85ccc3d..240a317e6e7a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -1,7 +1,7 @@ package org.dspace.app.rest.repository; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.Parameter; import org.dspace.app.rest.SearchRestMethod; @@ -10,12 +10,14 @@ import org.dspace.app.rest.exception.DSpaceBadRequestException; 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.util.Util; -import org.dspace.content.service.BitstreamService; +import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.*; import org.dspace.content.clarin.ClarinLicense; import org.dspace.content.clarin.ClarinLicenseResourceMapping; +import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; @@ -32,15 +34,14 @@ import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; @Component(MetadataBitstreamWrapperRest.CATEGORY + "." + MetadataBitstreamWrapperRest.NAME) public class MetadataBitstreamRestRepository extends DSpaceRestRepository{ @@ -59,19 +60,20 @@ public class MetadataBitstreamRestRepository extends DSpaceRestRepository findByHandle(@Parameter(value = "handle", required = true) String handle, @Parameter(value = "fileGrpType", required = false) String fileGrpType, Pageable pageable) - throws SQLException, ParserConfigurationException, IOException, SAXException { + throws SQLException, ParserConfigurationException, IOException, SAXException, AuthorizeException { if (StringUtils.isBlank(handle)) { throw new DSpaceBadRequestException("handle cannot be null!"); } + List metadataValueWrappers = new ArrayList<>(); Context context = obtainContext(); if (Objects.isNull(context)) { throw new RuntimeException("Cannot obtain the context from the request."); @@ -110,12 +112,6 @@ public Page findByHandle(@Parameter(value = "handl for (Bitstream bitstream : bitstreams) { -// try { -// InputStream inputStream = bitstreamService.retrieve(context, bitstream); -// log.error("inputStream: " + inputStream); -// } catch (Exception e) { -// throw new RuntimeException(e); -// } List clarinLicenseResourceMappings = licenseService.findByBitstreamUUID(context, bitstream.getID()); boolean canPreview = false; if ( clarinLicenseResourceMappings != null && clarinLicenseResourceMappings.size() > 0) { @@ -123,43 +119,44 @@ public Page findByHandle(@Parameter(value = "handl ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); canPreview = clarinLicense.getClarinLicenseLabels().stream().anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); } - String identifier = null; - if (item != null && item.getHandle() != null) - { - identifier = "handle/" + item.getHandle(); - } - else if (item != null) - { - identifier = "item/" + item.getID(); - } - else - { - identifier = "id/" + bitstream.getID(); - } - String url = contextPath + "/bitstream/"+identifier+"/"; - try - { - if (bitstream.getName() != null) + if (true) { + String identifier = null; + if (item != null && item.getHandle() != null) { - url += Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); + identifier = "handle/" + item.getHandle(); + } + else if (item != null) + { + identifier = "item/" + item.getID(); + } + else + { + identifier = "id/" + bitstream.getID(); + } + String url = contextPath + "/bitstream/"+identifier; + try + { + if (bitstream.getName() != null) + { + url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); + } + } + catch (UnsupportedEncodingException uee) + { + log.error("UnsupportedEncodingException", uee); } - } - catch (UnsupportedEncodingException uee) - { - log.error("UnsupportedEncodingException", uee); - } - url += "?sequence="+bitstream.getSequenceID(); + url += "?sequence="+bitstream.getSequenceID(); - String isAllowed = "n"; - try { - if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { - isAllowed = "y"; - } - } catch (SQLException e) {/* Do nothing */} + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + + url += "&isAllowed=" + isAllowed; - url += "&isAllowed=" + isAllowed; - if (true) { List metadataValues = bitstream.getMetadata(); // Filter out all metadata values that are not local to the bitstream // Uncomment this if we want to show metadata values that are local to the bitstream @@ -167,32 +164,41 @@ else if (item != null) // match("local", "bitstream", "file", metadataValue.getMetadataField())) // .collect(Collectors.toList()); List fileInfos = new ArrayList<>(); - if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { - List finalFileInfos = fileInfos; - metadataValues.stream().map(MetadataValue::getValue).reduce((s, s2) -> s + s2) - .ifPresent(s -> finalFileInfos.add(new FileInfo(s, false))); - fileInfos = finalFileInfos; + InputStream inputStream = bitstreamService.retrieve(context, bitstream); + if (bitstream.getFormat(context).getExtensions().contains("zip")) { + String data = extractFile(inputStream, bitstream.getName().substring(0, bitstream.getName().lastIndexOf("."))); + fileInfos = FileTreeViewGenerator.parse(data); } else { - StringBuilder sb = new StringBuilder(); - sb.append(""); - for (MetadataValue metadataValue : - metadataValues) { - sb.append(""); - sb.append(metadataValue.getValue()); - sb.append(""); - } - sb.append(""); - try { - fileInfos = FileTreeViewGenerator.parse(sb.toString()); - } catch (Exception e) { - fileInfos = null; + if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { + String data = getFileContent(inputStream); + fileInfos.add(new FileInfo(data, false)); } } +// if (bitstream.getFormat(context).getMIMEType().equals("text/plain")) { +// List finalFileInfos = fileInfos; +// metadataValues.stream().map(MetadataValue::getValue).reduce((s, s2) -> s + s2).ifPresent(s -> finalFileInfos.add(new FileInfo(s, false))); +// fileInfos = finalFileInfos; +// } else { +// StringBuilder sb = new StringBuilder(); +// sb.append(""); +// for (MetadataValue metadataValue : +// metadataValues) { +// sb.append(""); +// sb.append(metadataValue.getValue()); +// sb.append(""); +// } +// sb.append(""); +// try { +// fileInfos = FileTreeViewGenerator.parse(sb.toString()); +// } catch (Exception e) { +// log.error(e.getMessage(), e); +// fileInfos = null; +// } +// } MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + metadataValueWrappers.add(bts); rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); } else { - MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, null, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); - rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); continue; } } @@ -260,6 +266,61 @@ else if (!qualifier.equals(Item.ANY)) } + public String extractFile(InputStream inputStream, String folderRootName) { + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append(""); + sb.append(folderRootName + "/|0"); + sb.append(""); + try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) { + ZipEntry zipEntry; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + if (!zipEntry.isDirectory()) { + String subFileName = zipEntry.getName(); + + long uncompressedSize = calculateUncompressedSize(zipInputStream); + if (uncompressedSize > 0) { + sb.append(""); + sb.append(subFileName + "|" + uncompressedSize); + sb.append(""); + } + } else { + sb.append(""); + sb.append(zipEntry.getName() + "|0"); + sb.append(""); + } + zipInputStream.closeEntry(); + } + } catch (IOException e) { + e.printStackTrace(); + } + sb.append(""); + return sb.toString(); + } + + private static long calculateUncompressedSize(InputStream inputStream) throws IOException { + byte[] buffer = new byte[4096]; + long uncompressedSize = 0; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + uncompressedSize += bytesRead; + } + return uncompressedSize; + } + + public String getFileContent(InputStream inputStream) throws IOException { + StringBuilder content = new StringBuilder(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + String line; + while ((line = reader.readLine()) != null) { + content.append(line).append("\n"); + } + + reader.close(); + return content.toString(); + } + @Override public MetadataBitstreamWrapperRest findOne(Context context, Integer integer) { return null; From f268f4e83534e1f094a456779e2fb41ca77e1fa8 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Mon, 24 Jul 2023 19:01:32 +0700 Subject: [PATCH 05/10] handle the restriction for download --- .../app/rest/MetadataBitstreamController.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index 86518f37092c..769734133f4f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -10,6 +10,7 @@ import org.dspace.app.rest.model.MetadataBitstreamWrapper; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.*; import org.dspace.content.service.BitstreamService; import org.dspace.core.Context; @@ -21,6 +22,7 @@ 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; @@ -46,7 +48,8 @@ public class MetadataBitstreamController { @Autowired HandleService handleService; - + @Autowired + private AuthorizeService authorizeService; @GetMapping("/handle/{id}/{subId}/{fileName}") public ResponseEntity downloadSingleFile( @PathVariable("id") String id, @@ -77,6 +80,13 @@ public ResponseEntity downloadSingleFile( bundles) { for (Bitstream bitstream: bundle.getBitstreams()) { + try { + authorizeService.authorizeAction(context, bitstream, Constants.READ); + } catch (AuthorizeException e) { + response.sendRedirect(request.getContextPath() + "/login"); + } catch (SQLException e) { + response.sendRedirect(request.getContextPath() + "/login"); + } String btName = bitstream.getName(); if (btName.equalsIgnoreCase(fileName)) { try { @@ -145,6 +155,13 @@ public void downloadFileZip(@RequestParam("handleId") String handleId, for (Bundle original : bundles) { List bss = original.getBitstreams(); for (Bitstream bitstream : bss) { + try { + authorizeService.authorizeAction(context, bitstream, Constants.READ); + } catch (AuthorizeException e) { + response.sendRedirect(request.getContextPath() + "/login"); + } catch (SQLException e) { + response.sendRedirect(request.getContextPath() + "/login"); + } String filename = bitstream.getName(); ZipArchiveEntry ze = new ZipArchiveEntry(filename); zip.putArchiveEntry(ze); From 0fcaaa507b984ea8f3f0fc1ad5578c4deca85e40 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Wed, 26 Jul 2023 16:13:52 +0700 Subject: [PATCH 06/10] add redirect feature and handle large files upload --- .../app/rest/MetadataBitstreamController.java | 11 +- .../MetadataBitstreamRestRepository.java | 160 ++++++++++++------ .../rest/MetadataBitstreamControllerIT.java | 125 ++++++++++++++ 3 files changed, 237 insertions(+), 59 deletions(-) create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index 769734133f4f..8146a0de7a14 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -10,6 +10,7 @@ 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; @@ -82,10 +83,12 @@ public ResponseEntity downloadSingleFile( 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(request.getContextPath() + "/login"); + response.sendRedirect("http://localhost:4000" + "/login"); } catch (SQLException e) { - response.sendRedirect(request.getContextPath() + "/login"); + response.sendRedirect("http://localhost:4000" + "/login"); } String btName = bitstream.getName(); if (btName.equalsIgnoreCase(fileName)) { @@ -158,9 +161,9 @@ public void downloadFileZip(@RequestParam("handleId") String handleId, try { authorizeService.authorizeAction(context, bitstream, Constants.READ); } catch (AuthorizeException e) { - response.sendRedirect(request.getContextPath() + "/login"); + response.sendRedirect("http://localhost:4000" + "/login"); } catch (SQLException e) { - response.sendRedirect(request.getContextPath() + "/login"); + response.sendRedirect("http://localhost:4000" + "/login"); } String filename = bitstream.getName(); ZipArchiveEntry ze = new ZipArchiveEntry(filename); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 240a317e6e7a..80172aafcd3a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -30,16 +30,24 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.*; +import java.nio.file.*; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -119,44 +127,43 @@ public Page findByHandle(@Parameter(value = "handl ClarinLicense clarinLicense = licenseResourceMapping.getLicense(); canPreview = clarinLicense.getClarinLicenseLabels().stream().anyMatch(clarinLicenseLabel -> clarinLicenseLabel.getLabel().equals("PUB")); } - if (true) { - String identifier = null; - if (item != null && item.getHandle() != null) - { - identifier = "handle/" + item.getHandle(); - } - else if (item != null) - { - identifier = "item/" + item.getID(); - } - else - { - identifier = "id/" + bitstream.getID(); - } - String url = contextPath + "/bitstream/"+identifier; - try - { - if (bitstream.getName() != null) - { - url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); - } - } - catch (UnsupportedEncodingException uee) + String identifier = null; + if (item != null && item.getHandle() != null) + { + identifier = "handle/" + item.getHandle(); + } + else if (item != null) + { + identifier = "item/" + item.getID(); + } + else + { + identifier = "id/" + bitstream.getID(); + } + String url = contextPath + "/bitstream/"+identifier; + try + { + if (bitstream.getName() != null) { - log.error("UnsupportedEncodingException", uee); + url += "/" + Util.encodeBitstreamName(bitstream.getName(), "UTF-8"); } + } + catch (UnsupportedEncodingException uee) + { + log.error("UnsupportedEncodingException", uee); + } - url += "?sequence="+bitstream.getSequenceID(); - - String isAllowed = "n"; - try { - if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { - isAllowed = "y"; - } - } catch (SQLException e) {/* Do nothing */} + url += "?sequence="+bitstream.getSequenceID(); - url += "&isAllowed=" + isAllowed; + String isAllowed = "n"; + try { + if (authorizeService.authorizeActionBoolean(context, bitstream, Constants.READ)) { + isAllowed = "y"; + } + } catch (SQLException e) {/* Do nothing */} + url += "&isAllowed=" + isAllowed; + if (canPreview) { List metadataValues = bitstream.getMetadata(); // Filter out all metadata values that are not local to the bitstream // Uncomment this if we want to show metadata values that are local to the bitstream @@ -199,6 +206,9 @@ else if (item != null) metadataValueWrappers.add(bts); rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); } else { + MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, new ArrayList<>(), bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); + metadataValueWrappers.add(bts); + rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); continue; } } @@ -267,34 +277,74 @@ else if (!qualifier.equals(Item.ANY)) public String extractFile(InputStream inputStream, String folderRootName) { + List filePaths = new ArrayList<>(); + Path tempFile = null; + FileSystem zipFileSystem = null; + + try { + tempFile = Files.createTempFile("temp", ".zip"); + Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); + + zipFileSystem = FileSystems.newFileSystem(tempFile, (ClassLoader) null); + Path root = zipFileSystem.getPath("/"); + Files.walk(root) + .forEach(path -> { + try { + long fileSize = Files.size(path); + if (Files.isDirectory(path)) { + filePaths.add(path.toString().substring(1) + "/|" + fileSize ); + } else { + filePaths.add(path.toString().substring(1) + "|" + fileSize ); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (zipFileSystem != null) { + try { + zipFileSystem.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (tempFile != null) { + try { + Files.delete(tempFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + StringBuilder sb = new StringBuilder(); - sb.append(""); - sb.append(""); - sb.append(folderRootName + "/|0"); - sb.append(""); - try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) { - ZipEntry zipEntry; - while ((zipEntry = zipInputStream.getNextEntry()) != null) { - if (!zipEntry.isDirectory()) { - String subFileName = zipEntry.getName(); - - long uncompressedSize = calculateUncompressedSize(zipInputStream); - if (uncompressedSize > 0) { - sb.append(""); - sb.append(subFileName + "|" + uncompressedSize); - sb.append(""); - } - } else { + sb.append(("")); + int count = 0; + List allFiles = filePaths; + for (String filePath : allFiles) { + if (!filePath.isEmpty() && filePath.length() > 3) { + if (filePath.contains(".")) { + count ++; + } + sb.append(""); + sb.append(filePath); + sb.append(""); + + if (count > 10) { sb.append(""); - sb.append(zipEntry.getName() + "|0"); + sb.append("/|0"); sb.append(""); + sb.append(""); + sb.append("...too many files...|0"); + sb.append(""); + break; } - zipInputStream.closeEntry(); } - } catch (IOException e) { - e.printStackTrace(); } - sb.append(""); + sb.append(("")); return sb.toString(); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java new file mode 100644 index 000000000000..100fb7193283 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java @@ -0,0 +1,125 @@ +package org.dspace.app.rest; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.service.BitstreamService; +import org.dspace.core.Constants; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.result.ContentResultMatchers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.List; +import java.util.zip.Deflater; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class MetadataBitstreamControllerIT extends AbstractControllerIntegrationTest{ + private static final String METADATABITSTREAM_ENDPOINT = "/bitstream/"; + private static final String METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/handle"; + private static final String METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT = METADATABITSTREAM_ENDPOINT + "/allzip"; + private static final String AUTHOR = "Test author name"; + private Collection col; + + private Item publicItem; + private Bitstream bts; + + @Autowired + AuthorizeService authorizeService; + + @Autowired + BitstreamService bitstreamService; + + + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build(); + + publicItem = ItemBuilder.createItem(context, col) + .withAuthor(AUTHOR) + .build(); + + String bitstreamContent = "ThisIsSomeDummyText"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bts = BitstreamBuilder. + createBitstream(context, publicItem, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("application/zip") + .build(); + } + context.restoreAuthSystemState(); + } + + @Test + public void downloadSingleFileNullPathVariable() throws Exception { + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT)) + .andExpect(status().isBadRequest()); + } + + @Test + public void downloadSingleFileWithAuthorize() throws Exception { + InputStream ip = bitstreamService.retrieve(context, bts); + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/octet-stream;charset=UTF-8")) + .andExpect(content().bytes(IOUtils.toByteArray(ip))); + } + + @Test + public void downloadSingleFileWithNoAuthorize() throws Exception { + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT + "/" + publicItem.getHandle() + "/" + bts.getName())) + .andExpect(status().is3xxRedirection()); + } + + @Test + public void downloadAllZip() throws Exception { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZipArchiveOutputStream zip = new ZipArchiveOutputStream(byteArrayOutputStream); + zip.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS); + zip.setLevel(Deflater.NO_COMPRESSION); + ZipArchiveEntry ze = new ZipArchiveEntry(bts.getName()); + zip.putArchiveEntry(ze); + InputStream is = bitstreamService.retrieve(context, bts); + org.apache.commons.compress.utils.IOUtils.copy(is, zip); + zip.closeArchiveEntry(); + is.close(); + zip.close(); + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(get(METADATABITSTREAM_DOWNLOAD_ALL_ENDPOINT ).param("handleId", publicItem.getHandle())) + .andExpect(status().isOk()) + .andExpect(content().bytes(byteArrayOutputStream.toByteArray())); + + } + + +} From 6e324b0e92e973e31f3fcb42735ab49cc70f24b9 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Thu, 27 Jul 2023 11:50:02 +0700 Subject: [PATCH 07/10] add license header for new files --- dspace-api/src/main/java/org/dspace/util/FileInfo.java | 7 +++++++ .../java/org/dspace/util/FileTreeViewGenerator.java | 7 +++++++ .../dspace/app/rest/MetadataBitstreamController.java | 9 ++++++++- .../converter/MetadataBitstreamWrapperConverter.java | 7 +++++++ .../app/rest/model/MetadataBitstreamWrapperRest.java | 7 +++++++ .../hateoas/MetadataBitstreamWrapperResource.java | 7 +++++++ .../rest/model/wrapper/MetadataBitstreamWrapper.java | 7 +++++++ .../repository/MetadataBitstreamRestRepository.java | 7 +++++++ .../dspace/app/rest/MetadataBitstreamControllerIT.java | 10 ++++++++-- 9 files changed, 65 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/util/FileInfo.java b/dspace-api/src/main/java/org/dspace/util/FileInfo.java index 2408430700e4..4bc33f79449c 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileInfo.java +++ b/dspace-api/src/main/java/org/dspace/util/FileInfo.java @@ -1,3 +1,10 @@ +/** + * 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; diff --git a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java index db302310aac5..191bd44eaa51 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java +++ b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java @@ -1,3 +1,10 @@ +/** + * 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; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index 8146a0de7a14..11654395adeb 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -1,3 +1,10 @@ +/** + * 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; @@ -84,7 +91,7 @@ public ResponseEntity downloadSingleFile( try { authorizeService.authorizeAction(context, bitstream, Constants.READ); } catch (MissingLicenseAgreementException e) { - response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download"); + response.sendRedirect("http://localhost:4000/bitstream/" + bitstreamb.getID() + "/download"); } catch (AuthorizeException e) { response.sendRedirect("http://localhost:4000" + "/login"); } catch (SQLException e) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java index dd6ab981288f..50007f3dfe6f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -1,3 +1,10 @@ +/** + * 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; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java index e50f19c5407b..e2a011c4f770 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java @@ -1,3 +1,10 @@ +/** + * 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.model; import org.dspace.app.rest.RestResourceController; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java index 3d0aa7335f5b..1c09c9d95f5a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/MetadataBitstreamWrapperResource.java @@ -1,3 +1,10 @@ +/** + * 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.model.hateoas; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java index 77e66e8ecb60..0c8b684162f1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/MetadataBitstreamWrapper.java @@ -1,3 +1,10 @@ +/** + * 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.model; import org.dspace.content.Bitstream; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 80172aafcd3a..bee16f565b27 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -1,3 +1,10 @@ +/** + * 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.repository; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java index 100fb7193283..bad69cbb33ad 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java @@ -1,3 +1,10 @@ +/** + * 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.codec.CharEncoding; @@ -80,8 +87,7 @@ public void setUp() throws Exception { @Test public void downloadSingleFileNullPathVariable() throws Exception { - getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT)) - .andExpect(status().isBadRequest()); + getClient().perform(get(METADATABITSTREAM_DOWNLOAD_SINGLE_ENDPOINT)).andExpect(status().isNotFound()); } @Test From 912932b1d95daf2556b2d1bee4cb5ff5c3cb7b75 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Thu, 27 Jul 2023 15:04:01 +0700 Subject: [PATCH 08/10] format style code --- .../dspace/util/FileTreeViewGenerator.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java index 191bd44eaa51..4a74eb7b2b60 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java +++ b/dspace-api/src/main/java/org/dspace/util/FileTreeViewGenerator.java @@ -10,9 +10,10 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; -import java.util.Hashtable; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +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; @@ -20,22 +21,24 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - +/** + * Generate a tree view of the file in a bitstream + * + * @author longtv + */ public class FileTreeViewGenerator { + private FileTreeViewGenerator () { + } public static List 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(); @@ -43,18 +46,17 @@ public static List parse(String data) throws ParserConfigurationExcept 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) { + 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)) { + for (String p : path.split("/")) { + if (current.sub.containsKey(p)) { current = current.sub.get(p); } else { FileInfo temp = new FileInfo(p); @@ -62,19 +64,17 @@ public static List parse(String data) throws ParserConfigurationExcept current = temp; } } - - if(!fileName.isEmpty()) { + if (!fileName.isEmpty()) { FileInfo temp = new FileInfo(fileName, humanReadableFileSize(size)); current.sub.put(fileName, temp); + countFile++; } - - } while((n=n.getNextSibling())!=null); - + } 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) { + if (Math.abs(bytes) < thresh) { return bytes + " B"; } String units[] = {"kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; @@ -82,9 +82,8 @@ public static String humanReadableFileSize(long bytes) { do { bytes /= thresh; ++u; - } while(Math.abs(bytes) >= thresh && u < units.length - 1); + } while (Math.abs(bytes) >= thresh && u < units.length - 1); return bytes + " " + units[u]; } - } From a1eaefc4ccf1a59590aa7eecac9533c0ca7eb434 Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Thu, 27 Jul 2023 15:27:12 +0700 Subject: [PATCH 09/10] change wrong spelling --- .../java/org/dspace/app/rest/MetadataBitstreamController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java index 11654395adeb..f9d35517456c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java @@ -91,7 +91,7 @@ public ResponseEntity downloadSingleFile( try { authorizeService.authorizeAction(context, bitstream, Constants.READ); } catch (MissingLicenseAgreementException e) { - response.sendRedirect("http://localhost:4000/bitstream/" + bitstreamb.getID() + "/download"); + response.sendRedirect("http://localhost:4000/bitstream/" + bitstream.getID() + "/download"); } catch (AuthorizeException e) { response.sendRedirect("http://localhost:4000" + "/login"); } catch (SQLException e) { From dcb609263476f7a5db84f36c205805d716234f6e Mon Sep 17 00:00:00 2001 From: HuynhKhoa1601 Date: Thu, 27 Jul 2023 15:45:39 +0700 Subject: [PATCH 10/10] add license header --- .../dspace/app/rest/MetadataBitstreamRestRepositoryIT.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index 9ef31dc5c4ab..1f02fca2d726 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -1,3 +1,10 @@ +/** + * 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.codec.CharEncoding;