Goal
Add a filter executing prior to com.day.cq.dam.core.impl.servlet.CreateAssetServlet, intercepting the file upload stream and decode it (assuming the file content uploaded was already encoded for some purpose). CreateAssetServlet will then create a dam:Asset using the decoded input stream
A common business case is, scanning streams for viruses before adding them as assets in AEM. The uploaded stream should be read twice, first for virus scan and second time by CreateAssetServlet to create the asset
Snippets for apps.experienceaem.assets.EAEMDecryptRequestPart taken from org/apache/sling/engine/impl/parameters/RequestPartsIterator.java
Code in this post was not production harnessed, make sure you test (and improve it) for large files, small files, binaries, text files etc..
Demo | Package Install | Source Code | Github
Uploading an Encoded file
Encoded files are decoded prior to asset creation
Uploading a Normal file
Unencoded files upload is not supported with this filter installed
Solution
1) For test purposes encode an image using below code
FileInputStream fileIn = new FileInputStream(inputPath); byte[] bytes = IOUtils.toByteArray(fileIn); String encoded = Base64.getEncoder().encodeToString(bytes); FileOutputStream fileOut = new FileOutputStream(new File(encPath)); fileOut.write(encoded.getBytes()); fileOut.close();
2) Following code in filter decodes input stream and writes to a temp file, stream later read (by CreateAssetServlet ) is from this temp file
private InputStream getDecodedStream(Part part) throws IOException{ File tmpFile = File.createTempFile(TEMP_PREFIX, ".tmp"); byte[] decoded = Base64.getDecoder().decode(IOUtils.toByteArray(part.getInputStream())); FileOutputStream decodedStream = new FileOutputStream(tmpFile); decodedStream.write(decoded); decodedStream.close(); return new FileInputStream(tmpFile); }
3) apps.experienceaem.assets.DecryptAssetsFilter for decoding the uploaded files
package apps.experienceaem.assets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Service; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.Part; import javax.servlet.*; import java.io.*; import java.util.*; @Component( metatype = true, description = "Experience AEM Request Decrypt Filter for CreateAssetServlet", label = "EAEM CreateAssetServlet InputStream Decrypt Filter") @Service({Filter.class}) @Properties({ @Property(name = "sling.filter.scope",value = {"REQUEST"}, propertyPrivate = true) }) public class DecryptAssetsFilter implements Filter { private static Logger log = LoggerFactory.getLogger(DecryptAssetsFilter.class); public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(request instanceof SlingHttpServletRequest) || !(response instanceof SlingHttpServletResponse)) { chain.doFilter(request, response); return; } final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; if (!StringUtils.equals("POST", slingRequest.getMethod()) || !isCreateAssetRequest(slingRequest) ) { chain.doFilter(request, response); return; } log.info("Decoding create asset request - " + slingRequest.getRequestURI()); Iterator parts = (Iterator)request.getAttribute("request-parts-iterator"); if( (parts == null) || !parts.hasNext()){ chain.doFilter(request, response); return; } List<Part> otherParts = new ArrayList<Part>(); Part part = null; while(parts.hasNext()) { part = (Part) parts.next(); otherParts.add(new EAEMDecryptRequestPart(part)); } request.setAttribute("request-parts-iterator", otherParts.iterator()); chain.doFilter(request, response); } private boolean isCreateAssetRequest(SlingHttpServletRequest slingRequest){ String[] selectors = slingRequest.getRequestPathInfo().getSelectors(); if(ArrayUtils.isEmpty(selectors) || (selectors.length > 1)){ return false; } return selectors[0].equals("createasset"); } public void destroy() { } private static class EAEMDecryptRequestPart implements Part { private final Part part; private final InputStream inputStream; private static final String TEMP_PREFIX = "eaem_decrypt_"; public EAEMDecryptRequestPart(Part part) throws IOException { this.part = part; if(!isFilePart(part)){ this.inputStream = new ByteArrayInputStream(IOUtils.toByteArray(part.getInputStream())); }else{ this.inputStream = this.getDecodedStream(part); } } private InputStream getDecodedStream(Part part) throws IOException{ File tmpFile = File.createTempFile(TEMP_PREFIX, ".tmp"); byte[] decoded = Base64.getDecoder().decode(IOUtils.toByteArray(part.getInputStream())); FileOutputStream decodedStream = new FileOutputStream(tmpFile); decodedStream.write(decoded); decodedStream.close(); return new FileInputStream(tmpFile); } private boolean isFilePart(Part part) { return StringUtils.isNotEmpty(part.getSubmittedFileName()); } public InputStream getInputStream() throws IOException { return inputStream; } public String getContentType() { return part.getContentType(); } public String getName() { return part.getName(); } public long getSize() { return 0; } public void write(String s) throws IOException { throw new UnsupportedOperationException("Writing parts directly to disk is not supported by this implementation, use getInputStream instead"); } public void delete() throws IOException { } public String getHeader(String headerName) { return part.getHeader(headerName); } public Collection<String> getHeaders(String headerName) { return part.getHeaders(headerName); } public Collection<String> getHeaderNames() { return part.getHeaderNames(); } public String getSubmittedFileName() { return part.getSubmittedFileName(); } private <T> Collection<T> toCollection(Iterator<T> i) { if ( i == null ) { return Collections.emptyList(); } else { List<T> c = new ArrayList<T>(); while(i.hasNext()) { c.add(i.next()); } return c; } } } }
No comments:
Post a Comment