Sunday, October 30, 2011

How to read an external file in CQ

Use Case: You want to read something from external file

Solution : There are multiple ways you can do this I will mentioned two of them here,

1) You can put them under /resources folder under /src/main for example if you want to read a file called "test.txt" then you can put file under /src/resources/<any-sub-foler>/<file-to-read>

Then use following code to read file
InputStream stream = getClass().getResourceAsStream("<file-to-read>");
in above case it would be InputStream stream = getClass().getResourceAsStream("/test.txt");

Good practice is to follow convention, For example if your source code is under /src/main/java/com/test/mytest/Test.java Then your resource should be in location /resource/com/test/myTest/<Your File>

Then you can use something like


InputStream in = Test.class.getResourceAsStream("<Your File>");


2) You can put file in repository (Any repository location is file) and use Node data as stream to read.

then use following code,
InputStream in = node.getNode("jcr:content").getProperty("jcr:data").getStream();

How to Upload File in CRX


File file = new File("/mypath/mydocument.pdf");
FileInputStream is = new FileInputStream(file);
mimeType = "application/octet-stream";
Node node = session.getNode(parentNode);
ValueFactory valueFactory = session.getValueFactory();
Binary contentValue = valueFactory.createBinary(is);
Node fileNode = node.addNode(fileName, "nt:file");
fileNode.addMixin("mix:referenceable");
Node resNode = fileNode.addNode("jcr:content", "nt:resource");
resNode.setProperty("jcr:mimeType", mimeType);
resNode.setProperty("jcr:data", contentValue);
Calendar lastModified = Calendar.getInstance();
lastModified.setTimeInMillis(lastModified.getTimeInMillis());
resNode.setProperty("jcr:lastModified", lastModified);
session.save();
return fileNode.getPath();

Using Sling FSResource Provider (To convert File system resource to jcr resource):

Please see what all different kind of resource provider is present



FS resource provider essentially mapped your file system path with repository path. That mean you can adapt your file system resource to a sling resource object and can do fun stuff with it.

1) More information can be obtained here http://sling.apache.org/documentation/bundles/accessing-filesystem-resources-extensions-fsresource.html

2) FSResource provider is not present OOTB, so you have to first embed this in your project,

in your pom.xml do following (I hope you know how to do this, if not ping me :D),

<embedded>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.fsresource</artifactId>
<target>/apps/<your app>/install</target>
</embedded>

And

<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.fsresource</artifactId>
<version>1.1.3-SNAPSHOT</version>

</dependency>

3) create a osgi config with following setting 

Name of file: org.apache.sling.fsprovider.internal.FsResourceProvider-samples.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
          jcr:primaryType="sling:OsgiConfig"
          provider.roots=“<some repository path, this can be any random path>"
          provider.file=“<Absolute path for file in file system>"
          provider.checkinterval="1000”/>
4) Then in your code you can do following,

import java.io.File;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component 
@Service
public class FileSystemFileTest implements FileSystemTest {
private static final Logger LOG = LoggerFactory.getLogger(FileSystemFileTest.class);
@Reference
ResourceResolverFactory resourceResolverFactory;
@Reference
ResourceProvider resourceProvider;
ResourceResolver resourceResolver = null;
@Override
public File getFile() {
try {
resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
Resource resource =  resourceResolver.getResource("<some repository path from above>");
File file  = resource.adaptTo(File.class);
if(null!=file){
return file;
}
} catch (LoginException e) {
LOG.error(e.getMessage());
}finally{
if(null!=resourceResolver){
resourceResolver.close();
}
}
return null;
}
}

Note that in this case you need to have file in your file system. You will essentially use this approach if you want to get jcr resource view of your file system to do something with it. For example if your method can only accept resource object as argument.

5 comments:

  1. Great tip, was creating a blacklist for a component and #2 was the best solution I could find. Reading txt files from within CQ seems to be tricky I believe there are some permissions that need to be bypassed before it allows jsp to read from them. Placing my blacklist in the component properties and using InputStream worked perfectly. Thanks!

    ReplyDelete
  2. How can you achieve the described solution from point 1? I encountered a problem using it - the stream points to the actual bundle on the disk, not to a requested file inside the jar, e.g. C:\author-instance\crx-quickstart\launchpad\felix\bundle241\version8.8\bundle.jar

    ReplyDelete
    Replies
    1. You should be able to use this option as long as file path is correct within bundle. I am sure that your file exist in Resource folder of your package. You can also try class.getClassLoader.getresourceAsStream('Stream") as long as file is accesible within class path.

      Delete