Repository navigation
MCR-3858 Add archive (zip/tar) download endpoints to REST API v2 - #3162
Merged
sebhofmann merged 11 commits intoOct 22, 2026
Merged
Conversation
…act archiver classes from servlets Move the format specific archive entry code of MCRZipServlet and MCRTarServlet into the servlet independent classes MCRArchiver, MCRZipArchiver and MCRTarArchiver, so it can be reused outside of servlets. The servlets delegate to them, their protected API is unchanged.
…derivate archive resource
Add GET /objects/{mcrid}/derivates/{derid}/archive/zip and /archive/tar.
The archives contain all files of the derivate and are streamed.
Last-Modified is the latest modification date of all files and directories
of the derivate and conditional requests are supported.
… pom dependencies
This was referenced Oct 7, 2026
…e archives via contents Remove the separate archive endpoints. The contents resource of a derivate delivers a directory as archive if the Accept header is application/zip or application/x-tar. contents.zip and contents.tar are mapped to these media types by the content negotiation via extension filter, files in a derivate are not affected.
…rchive_endpoints_to_REST_API_v2
…ver errors for archive requests as JSON
…ess review - remove Accept header manipulation for archive requests from MCRContentNegotiationViaExtensionFilter - encode file name in Content-Disposition (RFC 6266/8187) via MCRRestUtils.getContentDisposition() - derive archive media types, variants and file extensions from the archivers (MIME_TYPE, FILE_EXTENSION constants) - do not finish an archive if streaming fails, so clients get a broken download instead of a valid but incomplete archive - shield the entity stream from closing, so the MCRSession is not ended too early - walk the directory tree only once (no Last-Modified for archives) - use the creation time in the archive comment - move MCRArchiver, MCRZipArchiver and MCRTarArchiver to org.mycore.datamodel.niofs.archive - MCRCompressServlet uses MCRArchiver.sendTree instead of its own visitor BREAKING CHANGE: MCRCompressServlet now requires T extends ArchiveOutputStream<?> and a getArchiver() implementation; the abstract methods sendCompressedDirectory, sendCompressedFile, sendMetadataCompressed, getMimeType, getFileExtension, createContainer and disposeContainer were removed.
… protected API of compress servlets Restore MCRCompressServlet, MCRZipServlet and MCRTarServlet with their previous protected API, because overlays extend them (reposis_ditav, reposis_soviet_survivors, dbt). Only the imports of the moved archiver classes change. This reverts the BREAKING CHANGE announced in the previous commit: there is no breaking change in this pull request anymore.
…ess second review Send Last-Modified for archives and answer conditional requests, deliver errors as JSON if only archives are accepted, close the license header of package-info.java and deprecate the unused getFilename of MCRCompressServlet.
…rt change of MCRExceptionMapper
… getFilename of MCRCompressServlet for removal
golsch
approved these changes
Oct 21, 2026
sebhofmann
deleted the
issues/MCR-3858-Add_archive_endpoints_to_REST_API_v2
branch
October 22, 2026 13:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Link to jira
Adds archive (zip/tar) download to the REST API v2, so that links to
MCRZipServletandMCRTarServletin export formats can later be replaced by REST API links.There is no separate archive endpoint. The existing
contentsresource of a derivate delivers a directory as archive:GET /api/v2/objects/{mcrid}/derivates/{derid}/contents(or any sub directory) withAccept: application/ziporAccept: application/x-tarstreams all files of the directory as archive. Without such an Accept header the directory is listed as before.GET /api/v2/objects/{mcrid}/derivates/{derid}/contents.zipandcontents.tarare shortcuts for the whole derivate. They are mapped to the Accept headers byMCRContentNegotiationViaExtensionFilter.There is no object level archive.
Design decisions
MCRZipServletandMCRTarServletis moved to the servlet independent classesMCRArchiver,MCRZipArchiverandMCRTarArchiverin the new packageorg.mycore.datamodel.niofs.archiveand reused byMCRRestDerivateContents. The servlets delegate to them. Their protected API is unchanged, because overlays extend them (e.g.reposis_ditav,reposis_soviet_survivors,dbt), soMCRCompressServletkeeps its own file visitor.MIME_TYPE,FILE_EXTENSION).@Produces, the content negotiation variants and the extension mapping ofMCRContentNegotiationViaExtensionFilterare derived from them.Content-Dispositioncontains an ASCII fallback infilenameand the exact name infilename*(RFC 6266/8187), seeMCRRestUtils.getContentDisposition().CloseShieldOutputStream, so closing the archive does not end theMCRSessiontoo early. If an error occurs while the archive is written, the archive is not finished, so the client receives a broken download instead of a valid but incomplete archive (the status 200 is already sent at that point).MCRRestAuthorizationFilter, as for every othercontentsrequest.contents.zipandcontents.tarof the derivate root. The regexMCR.RestAPI.V2.ContentNegotiationViaExtensionFilter.RegExstill ignores everything behind/contents/, so a file calledarchive.zipin a derivate is not touched. An application that sets its own regex has to addcontents.zipandcontents.tarto use the shortcuts. The Accept header works without it.Last-Modifiedheader and conditional requests are not supported, because that would need a second walk over the whole directory tree.Cache-Controlis the one of thecontentsresource (one day), as for single files. The archive comment contains the creation time.contents.zip), an error response cannot be written in that type and Jersey answers with a server error. Files behave the same way when a client sends a single specific Accept type. Fixing this belongs to the general error handling of the REST API (MCRErrorResponseis written with the negotiated type, even if that type cannot hold an error) and is out of scope of this PR.How to test
curl -OJ -H "Accept: application/zip" <base>/api/v2/objects/{mcrid}/derivates/{derid}/contentsand withapplication/x-tarcurl -OJ <base>/api/v2/objects/{mcrid}/derivates/{derid}/contents.zipandcontents.tarcurl <base>/api/v2/objects/{mcrid}/derivates/{derid}/contentswithout Accept header: expect the directory listingContent-Dispositioncontainsfilename*=UTF-8''...The entries of the archives are identical to the ones of
MCRZipServlet/{derid}andMCRTarServlet/{derid}.Status of testing: Before the review, tested locally against a deployed MIR with the MyCoRe branches of this PR and MCR-3860, as guest and as logged in user:
contents.zip,contents.tar,Accept: application/zipandapplication/x-tarfor the derivate and for a sub directory (entries identical toMCRZipServlet/MCRTarServlet), directory listing without Accept header, files (also a file calledarchive.zip, with and without an archive Accept header), 304 forIf-Modified-Since, 404 (unknown derivate, derivate of another object, unknown directory) and 401 (derivate not readable for guests) as JSON,contents.xmlandcontents.jsonas before. At that time the build ofmycore-restapiandmycore-basewas run without tests (-DskipTests), only checkstyle and PMD.The changes from the review (removed Accept header fallback, encoded
Content-Disposition, shared mime types, streaming withoutLast-Modified, new package of the archivers) are only verified by the build with all tests ofmycore-baseandmycore-restapi(including the newMCRRestUtilsTest), not yet against a deployed MIR. Inmycore-basethe only failing test locally isMCRCheckPermissionChainURIResolverTest.resolveReadObjectAllowed, which fails the same way without this PR.Pull Request Checklist (Author)
Ticket & Documentation
fixed-versionis correctly set in the ticket and matches the PR's target branch (main).Feature & Improvement Specific Checks
Testing
MCR Conventions & Metadata
mycore.properties.mycore.properties.Multi-Repo Considerations
MIRrequired?