Commit af3e1ce3 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

big models are returned as zip files

parent 3d5b4c0a
minerva (15.0.0~alpha.0) stable; urgency=medium
* Small improvement: when downloading a map results in too big file (>1MB)
the content is compressed and returned as a zip file (#348)
* Bug fix: position of structural state is preserved on upload CellDesigner
file (#671)
......
package lcsb.mapviewer.api;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -21,7 +22,9 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.Gson;
import lcsb.mapviewer.common.Configuration;
import lcsb.mapviewer.common.MimeType;
import lcsb.mapviewer.common.exception.InvalidStateException;
import lcsb.mapviewer.model.cache.FileEntry;
public abstract class BaseController {
private Logger logger = LogManager.getLogger(BaseController.class);
......@@ -114,4 +117,22 @@ public abstract class BaseController {
return result;
}
protected ResponseEntity<byte[]> sendAsZip(FileEntry originalFile) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry entry = new ZipEntry(originalFile.getOriginalFileName());
zos.putNextEntry(entry);
zos.write(originalFile.getFileContent());
zos.closeEntry();
zos.close();
return ResponseEntity.ok().contentLength(baos.size())
.contentType(MediaType.parseMediaType(MimeType.ZIP.getTextRepresentation()))
.header("Content-Disposition", "attachment; filename=" + originalFile.getOriginalFileName() + ".zip")
.body(baos.toByteArray());
}
}
package lcsb.mapviewer.api.projects.models;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
......@@ -12,12 +12,12 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import lcsb.mapviewer.api.*;
import lcsb.mapviewer.api.BaseController;
import lcsb.mapviewer.api.QueryException;
import lcsb.mapviewer.commands.CommandExecutionException;
import lcsb.mapviewer.converter.ConverterException;
import lcsb.mapviewer.common.MimeType;
import lcsb.mapviewer.converter.graphics.DrawingException;
import lcsb.mapviewer.model.cache.FileEntry;
import lcsb.mapviewer.model.map.InconsistentModelException;
import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.services.interfaces.IUserService;
......@@ -26,6 +26,8 @@ import lcsb.mapviewer.services.interfaces.IUserService;
@RequestMapping(value = "/projects/{projectId:.+}/models", produces = MediaType.APPLICATION_JSON_VALUE)
public class ModelController extends BaseController {
Logger logger = LogManager.getLogger();
private ModelRestImpl modelController;
private IUserService userService;
......@@ -87,7 +89,8 @@ public class ModelController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('IS_ADMIN', 'READ_PROJECT:' + #projectId)")
@RequestMapping(value = "/{modelId:.+}:downloadModel", method = { RequestMethod.GET, RequestMethod.POST })
@RequestMapping(value = "/{modelId:.+}:downloadModel", method = { RequestMethod.GET,
RequestMethod.POST }, produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE, "application/zip" })
public ResponseEntity<byte[]> getModelAsModelFile(
Authentication authentication,
@PathVariable(value = "projectId") String projectId,
......@@ -97,16 +100,33 @@ public class ModelController extends BaseController {
@RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId,
@RequestParam(value = "polygonString", defaultValue = "") String polygonString,
@RequestParam(value = "elementIds", defaultValue = "") String elementIds,
@RequestParam(value = "reactionIds", defaultValue = "") String reactionIds)
throws QueryException, IOException, InvalidColorSchemaException, CommandExecutionException,
ConverterException, InconsistentModelException {
@RequestParam(value = "reactionIds", defaultValue = "") String reactionIds,
@RequestHeader("Accept") String accept)
throws Exception {
User user = userService.getUserByLogin(authentication.getName());
FileEntry file = modelController.getModelAsModelFile(
projectId, modelId, handlerClass, overlayIds, polygonString, elementIds, reactionIds, user, backgroundOverlayId);
projectId, modelId, handlerClass, overlayIds, polygonString, elementIds, reactionIds, user,
backgroundOverlayId);
if (isMimeTypeIncluded(accept, MimeType.ZIP.getTextRepresentation())) {
return sendAsZip(file);
}
if (file.getFileContent().length >= 1024 * 1024) {
return sendAsZip(file);
}
return ResponseEntity.ok().contentLength(file.getFileContent().length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
.body(file.getFileContent());
}
private boolean isMimeTypeIncluded(String accept, String mimeType) {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(accept);
for (MediaType mediaType : mediaTypes) {
if (Objects.equals(mediaType.getType() + "/" + mediaType.getSubtype(), mimeType)) {
return true;
}
}
return false;
}
}
\ No newline at end of file
......@@ -15,12 +15,14 @@ import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonParser;
import lcsb.mapviewer.common.Configuration;
import lcsb.mapviewer.common.MimeType;
import lcsb.mapviewer.converter.graphics.PngImageGenerator;
import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser;
import lcsb.mapviewer.model.Project;
......@@ -278,13 +280,29 @@ public class MapControllerIntegrationTest extends ControllerIntegrationTest {
RequestBuilder request = get("/projects/" + TEST_PROJECT + "/models/" + map.getId() + ":downloadModel?" +
"handlerClass=" + CellDesignerXmlParser.class.getCanonicalName())
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.session(session);
mockMvc.perform(request)
.andExpect(status().is2xxSuccessful());
}
@Test
public void testDownloadModelAsZip() throws Exception {
MockHttpSession session = createSession(ControllerIntegrationTest.BUILT_IN_TEST_ADMIN_LOGIN,
ControllerIntegrationTest.BUILT_IN_TEST_ADMIN_PASSWORD);
RequestBuilder request = get("/projects/" + TEST_PROJECT + "/models/" + map.getId() + ":downloadModel?" +
"handlerClass=" + CellDesignerXmlParser.class.getCanonicalName())
.accept(MimeType.ZIP.getTextRepresentation()+";q=0.8")
.session(session);
MvcResult result = mockMvc.perform(request)
.andExpect(status().is2xxSuccessful())
.andReturn();
assertEquals(MimeType.ZIP.getTextRepresentation(), result.getResponse().getContentType());
assertTrue(result.getResponse().getHeader("Content-Disposition").endsWith(".zip"));
}
@Test
public void testDownloadModelWithUndefinedProject() throws Exception {
MockHttpSession session = createSession(ControllerIntegrationTest.BUILT_IN_TEST_ADMIN_LOGIN,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment