0,0 → 1,268 |
/* |
* Copyright 2013 Brian Rosenberger (Brutex Network) |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
* You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software |
* distributed under the License is distributed on an "AS IS" BASIS, |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
* See the License for the specific language governing permissions and |
* limitations under the License. |
*/ |
|
package net.brutex.xservices.ws.rs; |
|
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.OutputStream; |
import java.net.URI; |
import java.nio.file.DirectoryStream; |
import java.nio.file.FileSystems; |
import java.nio.file.FileVisitOption; |
import java.nio.file.FileVisitResult; |
import java.nio.file.Files; |
import java.nio.file.Path; |
import java.nio.file.PathMatcher; |
import java.nio.file.SimpleFileVisitor; |
import java.nio.file.attribute.BasicFileAttributeView; |
import java.nio.file.attribute.BasicFileAttributes; |
import java.util.ArrayList; |
import java.util.EnumSet; |
import java.util.List; |
import java.util.zip.ZipEntry; |
import java.util.zip.ZipOutputStream; |
|
import javax.ws.rs.NotAuthorizedException; |
import javax.ws.rs.WebApplicationException; |
import javax.ws.rs.core.GenericEntity; |
import javax.ws.rs.core.HttpHeaders; |
import javax.ws.rs.core.MediaType; |
import javax.ws.rs.core.Response; |
import javax.ws.rs.core.StreamingOutput; |
import javax.ws.rs.core.UriInfo; |
|
import net.brutex.xservices.security.DirectoryPermission; |
import net.brutex.xservices.types.FileInfoType; |
import net.brutex.xservices.util.FileWalker; |
|
import org.apache.jcs.JCS; |
import org.apache.jcs.access.exception.CacheException; |
import org.apache.log4j.Logger; |
import org.apache.shiro.SecurityUtils; |
import org.apache.shiro.authz.UnauthorizedException; |
|
/** |
* The Class FileInfoImpl. |
* |
* @author Brian Rosenberger, bru(at)brutex.de |
*/ |
public class FileInfoImpl implements FileInfo { |
|
|
Logger logger = Logger.getLogger(FileInfoImpl.class); |
|
|
/* (non-Javadoc) |
* @see net.brutex.xservices.ws.rs.FileInfo#getFiles(javax.ws.rs.core.HttpHeaders, java.lang.String, boolean, boolean, int, java.lang.String, int, int) |
*/ |
public Response getFiles(HttpHeaders h, UriInfo uriInfo, String dir, boolean withDir, boolean withFiles, int level, String search, int count, int page, boolean useCache) |
{ |
if(dir==null) { |
dir = "c:/"; |
logger.warn("No directory specified. Default is 'c:/'."); |
} |
isPermitted(dir); |
|
URI baseuri = URI.create(uriInfo.getBaseUri()+FileInfo.BASE_PATH+"getFile?file="); |
|
logger.info(String.format("Listing directory '%s'.", dir)); |
if (level <= 0) level = 1; |
|
if ((!withDir) && (!withFiles)) withFiles = true; |
String cachekey = level + "||" + withFiles + "||" + withDir + "||" + search + "||" + dir; |
try { |
logger.debug(String.format("Hitting cache with cachekey '%s'", cachekey)); |
JCS jcs = JCS.getInstance("FileCache"); |
|
/*Try to retrieve the file list from the cache*/ |
List<FileInfoType> list = (List<FileInfoType>)jcs.get(cachekey); |
|
if (list == null || !useCache) { |
list = setDirectory(baseuri, dir, withDir, withFiles, level, search); |
jcs.put(cachekey, list); |
logger.debug("Stored in Cache: " + list.toString()); |
} else { |
logger.debug("Got from Cache: " + list.toString()); |
} |
|
int fromIndex = 0; |
int toIndex = 0; |
fromIndex = (page - 1) * count; |
toIndex = page * count; |
if (toIndex > list.size()) toIndex = list.size(); |
if (fromIndex > toIndex) fromIndex = toIndex; |
GenericEntity<List<FileInfoType>> sublist = new GenericEntity<List<FileInfoType>>(list.subList(fromIndex, toIndex)) {}; |
logger.info(String.format("Returning items %s to %s from total of %s items in the list.", fromIndex, toIndex, list.size())); |
return Response.ok(sublist).build(); |
} catch (CacheException e) { |
return Response.serverError().build(); |
} |
} |
|
/** |
* Sets the directory. |
* |
* @param list the list |
* @param dir the dir |
* @param withDirectories the with directories |
* @param withFiles the with files |
* @param depth the depth |
* @param search the search |
*/ |
private void setDirectory(final URI baseuri, final List<FileInfoType> list, File dir, boolean withDirectories, boolean withFiles, final int depth, String search) |
{ |
if (depth <= 0) return; |
|
if(search==null || search.equals("") ) { |
search = "*"; |
logger.info("No search pattern supplied, using default '*'."); |
} |
|
FileWalker finder = new FileWalker(search); |
try { |
Files.walkFileTree(dir.toPath(), EnumSet.of(FileVisitOption.FOLLOW_LINKS), depth, finder); |
logger.info("FileWalker returned '"+finder.getCount()+"' hits. '" + finder.getTotal() + "' files have been scanned."); |
List<Path> result = finder.getResult(); |
for(Path f : result) { |
if(! withDirectories) { |
if(f.toFile().isDirectory()) continue; |
} |
if(! withFiles) { |
if(f.toFile().isFile()) continue; |
} |
list.add(new FileInfoType(f, baseuri)); |
} |
} catch (IOException e2) { |
logger.error(e2.getMessage(), e2);; |
} |
} |
|
/** |
* Sets the directory. |
* |
* @param dir the dir |
* @param withDirectories the with directories |
* @param withFiles the with files |
* @param depth the depth |
* @param search the search |
* @return the list |
*/ |
private List<FileInfoType> setDirectory(URI baseuri, String dir, boolean withDirectories, boolean withFiles, int depth, String search) |
{ |
List<FileInfoType> list = new ArrayList<FileInfoType>(); |
setDirectory(baseuri, list, new File(dir), withDirectories, withFiles, depth, search); |
return list; |
} |
|
@Override |
public Response getFile(HttpHeaders paramHttpHeaders, String file) { |
isPermitted(file); |
try { |
Path path = FileSystems.getDefault().getPath(file); |
|
BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class); |
BasicFileAttributes basic; |
basic = basicView.readAttributes(); |
|
|
//In case this is a directory |
//we zip it and return the zip stream |
if(basic.isDirectory()) return getDirectoryAsZip(path); |
|
|
|
MediaType mime = MediaType.APPLICATION_OCTET_STREAM_TYPE; |
try { |
mime = MediaType.valueOf(Files.probeContentType(path)); |
} catch (IllegalArgumentException | IOException e) { |
//In case we can not find the media type for some reason |
//the default assignment is taken, so we can |
//ignore this error. |
logger.debug(String.format("Could not probe media type for file '%s'. Default is '%s'", path.toString(), mime.getType()), e); |
} |
Response r = Response.ok(path.toFile(), mime).build(); |
String fileName = path.getFileName().toString(); |
if(mime == MediaType.APPLICATION_OCTET_STREAM_TYPE) r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + fileName + "\""); |
return r; |
} catch (IOException e1) { |
// TODO Auto-generated catch block |
logger.error(e1.getMessage(), e1); |
return Response.serverError().build(); |
} |
} |
|
private Response getDirectoryAsZip(final Path path) { |
|
StreamingOutput output = new StreamingOutput() { |
|
@Override |
public void write(OutputStream os) throws IOException, |
WebApplicationException { |
ZipOutputStream zos = new ZipOutputStream(os); |
|
//read directory content (files only) |
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { |
for (Path file: stream) { |
//skip anything not being a file |
if(! file.toFile().isFile()) continue; |
|
//ZipEntry |
String filename = file.getFileName().toString(); |
ZipEntry ze = new ZipEntry(filename); |
zos.putNextEntry( ze ); |
|
//read a file and put it into the output stream |
FileInputStream fis = new FileInputStream(file.toFile()); |
byte[] buffer = new byte[1024]; |
int len; |
while ((len = fis.read(buffer)) > 0) { |
zos.write(buffer, 0, len); |
} |
zos.flush(); |
fis.close(); |
} |
zos.close(); |
} |
|
} |
}; |
Response r = Response.ok(output, MediaType.APPLICATION_OCTET_STREAM_TYPE).build(); |
String zipname = (path.getFileName()==null) ? "null.zip" : path.getFileName().toString()+".zip"; |
r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + zipname + "\""); |
return r; |
} |
|
private boolean isPermitted(String dir) { |
if(! SecurityUtils.getSubject().isPermitted( new DirectoryPermission(dir))) { |
logger.warn(String.format("User '%s' does not have permission to access '%s'.",SecurityUtils.getSubject().getPrincipal(), dir )); |
throw new NotAuthorizedException(new UnauthorizedException("User does not have permission to access "+ dir)); |
} |
return true; |
} |
|
//http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java |
private static String humanReadableByteCount(long bytes, boolean si) { |
int unit = si ? 1000 : 1024; |
if (bytes < unit) return bytes + " B"; |
int exp = (int) (Math.log(bytes) / Math.log(unit)); |
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i"); |
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); |
} |
|
} |
Property changes: |
Added: svn:mime-type |
+text/plain |
\ No newline at end of property |