I am using spark to develop a web application; the problem occurs when I want to upload a file:
public final class SparkTesting
{
public static void main(final String... args)
{
Spark.staticFileLocation("/site");
Spark.port(8080);
Spark.post("/upload", (request, response) -> {
final Part uploadedFile = request.raw().getPart("uploadedFile");
final Path path = Paths.get("/tmp/meh");
try (final InputStream in = uploadedFile.getInputStream()) {
Files.copy(in, path);
}
response.redirect("/");
return "OK";
});
}
}
But I get this error:
[qtp509057984-36] ERROR spark.webserver.MatcherFilter -
java.lang.IllegalStateException: No multipart config for servlet
at org.eclipse.jetty.server.Request.getPart(Request.java:2039)
at javax.servlet.http.HttpServletRequestWrapper.getPart(HttpServletRequestWrapper.java:361)
at com.github.fge.grappa.debugger.web.SparkTesting.lambda$main$0(SparkTesting.java:20)
at com.github.fge.grappa.debugger.web.SparkTesting$$Lambda$1/920011586.handle(Unknown Source)
at spark.SparkBase$1.handle(SparkBase.java:264)
at spark.webserver.MatcherFilter.doFilter(MatcherFilter.java:154)
at spark.webserver.JettyHandler.doHandle(JettyHandler.java:60)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:179)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:52)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:451)
at org.eclipse.jetty.server.HttpChannel.run(HttpChannel.java:252)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:266)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:240)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
at java.lang.Thread.run(Thread.java:745)
And even if I try and specify the type explicitly, as in:
Spark.post("/upload", "multipart/form-data", etc etc)
it will still fail.
I could probably find a library to parse multipart/form-data, grab the whole content and just parse myself, but that'd be a waste.
Can I configure spark to handle that case?
The answer provided by Kai Yao is correct except that when using:
request.raw().setAttribute("org.eclipse.multipartConfig", multipartConfigElement);
use this instead:
request.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
By adding a few lines of code to add the multipart config, you can handle multipart/form-data without an external library:
public Object handle(Request request, Response response) {
MultipartConfigElement multipartConfigElement = new MultipartConfigElement("/tmp");
request.raw().setAttribute("org.eclipse.multipartConfig", multipartConfigElement);
....
Part file = request.raw().getPart("file"); //file is name of the upload form
}
Source: http://deniz.dizman.org/file-uploads-using-spark-java-micro-framework/
I used apache commons-fileupload to handle this.
post("/upload", (req, res) -> {
final File upload = new File("upload");
if (!upload.exists() && !upload.mkdirs()) {
throw new RuntimeException("Failed to create directory " + upload.getAbsolutePath());
}
// apache commons-fileupload to handle file upload
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(upload);
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> items = fileUpload.parseRequest(req.raw());
// image is the field name that we want to save
FileItem item = items.stream()
.filter(e -> "image".equals(e.getFieldName()))
.findFirst().get();
String fileName = item.getName();
item.write(new File(dir, fileName));
halt(200);
return null;
});
See https://github.com/perwendel/spark/issues/26#issuecomment-95077039
I found complete example here:
https://github.com/tipsy/spark-file-upload/blob/master/src/main/java/UploadExample.java
import spark.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.nio.file.*;
import static spark.Spark.*;
import static spark.debug.DebugScreen.*;
public class UploadExample {
public static void main(String[] args) {
enableDebugScreen();
File uploadDir = new File("upload");
uploadDir.mkdir(); // create the upload directory if it doesn't exist
staticFiles.externalLocation("upload");
get("/", (req, res) ->
"<form method='post' enctype='multipart/form-data'>" // note the enctype
+ " <input type='file' name='uploaded_file' accept='.png'>" // make sure to call getPart using the same "name" in the post
+ " <button>Upload picture</button>"
+ "</form>"
);
post("/", (req, res) -> {
Path tempFile = Files.createTempFile(uploadDir.toPath(), "", "");
req.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
try (InputStream input = req.raw().getPart("uploaded_file").getInputStream()) { // getPart needs to use same "name" as input field in form
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
}
logInfo(req, tempFile);
return "<h1>You uploaded this image:<h1><img src='" + tempFile.getFileName() + "'>";
});
}
// methods used for logging
private static void logInfo(Request req, Path tempFile) throws IOException, ServletException {
System.out.println("Uploaded file '" + getFileName(req.raw().getPart("uploaded_file")) + "' saved as '" + tempFile.toAbsolutePath() + "'");
}
private static String getFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
return cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
}
}
return null;
}
}
Please note that in this example in order to iterate over all files use javax.servlet.http.HttpServletRequest#getParts. Also in this example instead of parsing file name you can simply get it using javax.servlet.http.Part#getSubmittedFileName. And also do not forget to close the stream you get. And also delete the file using javax.servlet.http.Part#delete if needed
Related
So I am using Java for my Server and Angular for the Client. I am currently working on a feature where you can select multiple files from a table and when you press on download, it generates a zip file and downloads it to your browser. As of right now, the server now creates the zip file and I can access it in the server files. All that is left to do is to make it download on the client's browser. (the zip file is deleted after the client downloads it)
After doing some research, I found out that you can use a fileOutputStream to do this. I also saw some tools like retrofit... I am using REST and this is what my code looks like. How would I achieve my goal as simply as possible?
Angular
httpGetDownloadZip(target: string[]): Observable<ServerAnswer> {
const params = new HttpParams().set('target', String(target)).set('numberOfFiles', String(target.length));
const headers = new HttpHeaders().set('token', this.tokenService.getStorageToken());
const options = {
headers,
params,
};
return this.http
.get<ServerAnswer>(this.BASE_URL + '/files/downloadZip', options)
.pipe(catchError(this.handleError<ServerAnswer>('httpGetZip')));
}
Java zipping method
public void getDownloadZip(String[] files, String folderName) throws IOException {
[...] // The method is huge but basically I generate a folder called "Download/" in the server
// Zipping the "Download/" folder
ZipUtil.pack(new File("Download"), new File("selected-files.zip"));
// what do I return ???
return;
}
Java context
server.createContext("/files/downloadZip", new HttpHandler() {
#Override
public void handle(HttpExchange exchange) throws IOException {
if (!handleTokenPreflight(exchange)) { return; }
System.out.println(exchange.getRequestURI());
Map<String, String> queryParam = parseQueryParam(exchange.getRequestURI().getQuery());
String authToken = exchange.getRequestHeaders().getFirst("token");
String target = queryParam.get("target") + ",";
String[] files = new String[Integer.parseInt(queryParam.get("numberOfFiles"))];
[...] // I process the data in this entire method and send it to the previous method that creates a zip
Controller.getDownloadZip(files, folderName);
// what do I return to download the file on the client's browser ????
return;
}
});
A possible approach to successfully download your zip file can be the described in the following paragraphs.
First, consider returning a reference to the zip file obtained as the compression result in your downloadZip method:
public File getDownloadZip(String[] files, String folderName) throws IOException {
[...] // The method is huge but basically I generate a folder called "Download/" in the server
// Zipping the "Download/" folder
File selectedFilesZipFile = new File("selected-files.zip")
ZipUtil.pack(new File("Download"), selectedFilesZipFile);
// return the zipped file obtained as result of the previous operation
return selectedFilesZipFile;
}
Now, modify your HttpHandler to perform the download:
server.createContext("/files/downloadZip", new HttpHandler() {
#Override
public void handle(HttpExchange exchange) throws IOException {
if (!handleTokenPreflight(exchange)) { return; }
System.out.println(exchange.getRequestURI());
Map<String, String> queryParam = parseQueryParam(exchange.getRequestURI().getQuery());
String authToken = exchange.getRequestHeaders().getFirst("token");
String target = queryParam.get("target") + ",";
String[] files = new String[Integer.parseInt(queryParam.get("numberOfFiles"))];
[...] // I process the data in this entire method and send it to the previous method that creates a zip
// Get a reference to the zipped file
File selectedFilesZipFile = Controller.getDownloadZip(files, folderName);
// Set the appropiate Content-Type
exchange.getResponseHeaders().set("Content-Type", "application/zip");
// Optionally, if the file is downloaded in an anchor, set the appropiate content disposition
// exchange.getResponseHeaders().add("Content-Disposition", "attachment; filename=selected-files.zip");
// Download the file. I used java.nio.Files to copy the file contents, but please, feel free
// to use other option like java.io or the Commons-IO library, for instance
exchange.sendResponseHeaders(200, selectedFilesZipFile.length());
try (OutputStream responseBody = httpExchange.getResponseBody()) {
Files.copy(selectedFilesZipFile.toPath(), responseBody);
responseBody.flush();
}
}
});
Now the problem is how to deal with the download in Angular.
As suggested in the previous code, if the resource is public or you have a way to manage your security token, including it as a parameter in the URL, for instance, one possible solution is to not use Angular HttpClient but an anchor with an href that points to your ever backend handler method directly.
If you need to use Angular HttpClient, perhaps to include your auth tokens, then you can try the approach proposed in this great SO question.
First, in your handler, encode to Base64 the zipped file contents to simplify the task of byte handling (in a general use case, you can typically return from your server a JSON object with the file content and metadata describing that content, like content-type, etcetera):
server.createContext("/files/downloadZip", new HttpHandler() {
#Override
public void handle(HttpExchange exchange) throws IOException {
if (!handleTokenPreflight(exchange)) { return; }
System.out.println(exchange.getRequestURI());
Map<String, String> queryParam = parseQueryParam(exchange.getRequestURI().getQuery());
String authToken = exchange.getRequestHeaders().getFirst("token");
String target = queryParam.get("target") + ",";
String[] files = new String[Integer.parseInt(queryParam.get("numberOfFiles"))];
[...] // I process the data in this entire method and send it to the previous method that creates a zip
// Get a reference to the zipped file
File selectedFilesZipFile = Controller.getDownloadZip(files, folderName);
// Set the appropiate Content-Type
exchange.getResponseHeaders().set("Content-Type", "application/zip");
// Download the file
byte[] fileContent = Files.readAllBytes(selectedFilesZipFile.toPath());
byte[] base64Data = Base64.getEncoder().encode(fileContent);
exchange.sendResponseHeaders(200, base64Data.length);
try (OutputStream responseBody = httpExchange.getResponseBody()) {
// Here I am using Commons-IO IOUtils: again, please, feel free to use other alternatives for writing
// the base64 data to the response outputstream
IOUtils.write(base64Data, responseBody);
responseBody.flush();
}
}
});
After that, use the following code in you client side Angular component to perform the download:
this.downloadService.httpGetDownloadZip(['target1','target2']).pipe(
tap((b64Data) => {
const blob = this.b64toBlob(b64Data, 'application/zip');
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl);
})
).subscribe()
As indicated in the aforementioned question, b64toBlob will look like this:
private b64toBlob(b64Data: string, contentType = '', sliceSize = 512) {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
Probably you will need to slightly modify the httpGetDownloadZip method in your service to take into account the returned base 64 data - basically, change ServerAnswer to string as the returned information type:
httpGetDownloadZip(target: string[]): Observable<string> {
const params = new HttpParams().set('target', String(target)).set('numberOfFiles', String(target.length));
const headers = new HttpHeaders().set('token', this.tokenService.getStorageToken());
const options = {
headers,
params,
};
return this.http
.get<string>(this.BASE_URL + '/files/downloadZip', options)
.pipe(catchError(this.handleError<ServerAnswer>('httpGetZip')));
}
You could try using responseType as arraybuffer.
Eg:
return this.http.get(URL_API_REST + 'download?filename=' + filename, {
responseType: 'arraybuffer'
});
In My Project including both front end (angular) and back end (java).
We used the below solution ( hope it work for you ):
Angular:
https://github.com/eligrey/FileSaver.js
let observable = this.downSvc.download(opts);
this.handleData(observable, (data) => {
let content = data;
const blob = new Blob([content], { type: 'application/pdf' });
saveAs(blob, file);
});
Java:
public void download(HttpServletRequest request,HttpServletResponse response){
....
response.setHeader("Content-Disposition",
"attachment;filename=\"" + fileName + "\"");
try (
OutputStream os = response.getOutputStream();
InputStream is = new FileInputStream(file);) {
byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) > -1) {
os.write(buf, 0, len);
}
os.flush();
}
You can still use HttpServletRequest on the server...
Then get its OutputStream and write to it.
#RequestMapping(method = RequestMethod.POST , params="action=downloadDocument")
public String downloadDocument(#RequestParam(value="documentId", required=true) String documentId,
HttpServletRequest request,
HttpServletResponse response )
{
try {
String docName = null;
String documentSavePath = getDocumentSavePath();
PDocument doc = mainService.getDocumentById(iDocumentId);
if(doc==null){
throw new RuntimeException("document with id: " + documentId + " not found!");
}
docName = doc.getName();
String path = documentSavePath + ContextUtils.fileSeperator() + docName;
response.setHeader("Content-Disposition", "inline;filename=\"" + docName + "\"");
OutputStream out = response.getOutputStream();
response.setContentType("application/word");
FileInputStream stream = new FileInputStream(path);
IOUtils.copy(stream, out);
out.flush();
out.close();
} catch(FileNotFoundException fnfe){
logger.error("Error downloading document! - document not found!!!! " + fnfe.getMessage() , fnfe);
} catch (IOException e) {
logger.error("Error downloading document!!! " + e.getMessage(),e);
}
return null;
}
When I run Microsoft Azure Media Services code written using Java in local it is working but when I deploy the same code in dev environment , I am unable to access the Azure and its throwing java.net.HostNotFoundException.
What is the best approach to use network proxy to connect to Azure
Below is the code I am using via java and using azure-java-sdk
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.util.EnumSet;
import com.microsoft.windowsazure.Configuration;
import com.microsoft.windowsazure.exception.ServiceException;
import com.microsoft.windowsazure.services.media.MediaConfiguration;
import com.microsoft.windowsazure.services.media.MediaContract;
import com.microsoft.windowsazure.services.media.MediaService;
import com.microsoft.windowsazure.services.media.WritableBlobContainerContract;
import com.microsoft.windowsazure.services.media.models.AccessPolicy;
import com.microsoft.windowsazure.services.media.models.AccessPolicyInfo;
import com.microsoft.windowsazure.services.media.models.AccessPolicyPermission;
import com.microsoft.windowsazure.services.media.models.Asset;
import com.microsoft.windowsazure.services.media.models.AssetFile;
import com.microsoft.windowsazure.services.media.models.AssetFileInfo;
import com.microsoft.windowsazure.services.media.models.AssetInfo;
import com.microsoft.windowsazure.services.media.models.Job;
import com.microsoft.windowsazure.services.media.models.JobInfo;
import com.microsoft.windowsazure.services.media.models.JobState;
import com.microsoft.windowsazure.services.media.models.ListResult;
import com.microsoft.windowsazure.services.media.models.Locator;
import com.microsoft.windowsazure.services.media.models.LocatorInfo;
import com.microsoft.windowsazure.services.media.models.LocatorType;
import com.microsoft.windowsazure.services.media.models.MediaProcessor;
import com.microsoft.windowsazure.services.media.models.MediaProcessorInfo;
import com.microsoft.windowsazure.services.media.models.Task;
public class HelloMediaServices
{
// Media Services account credentials configuration
private static String mediaServiceUri = "https://media.windows.net/API/";
private static String oAuthUri = "https://wamsprodglobal001acs.accesscontrol.windows.net/v2/OAuth2-13";
private static String clientId = "account name";
private static String clientSecret = "account key";
private static String scope = "urn:WindowsAzureMediaServices";
private static MediaContract mediaService;
// Encoder configuration
private static String preferedEncoder = "Media Encoder Standard";
private static String encodingPreset = "H264 Multiple Bitrate 720p";
public static void main(String[] args)
{
try {
// Set up the MediaContract object to call into the Media Services account
Configuration configuration = MediaConfiguration.configureWithOAuthAuthentication(
mediaServiceUri, oAuthUri, clientId, clientSecret, scope);
mediaService = MediaService.create(configuration);
// Upload a local file to an Asset
AssetInfo uploadAsset = uploadFileAndCreateAsset("BigBuckBunny.mp4");
System.out.println("Uploaded Asset Id: " + uploadAsset.getId());
// Transform the Asset
AssetInfo encodedAsset = encode(uploadAsset);
System.out.println("Encoded Asset Id: " + encodedAsset.getId());
// Create the Streaming Origin Locator
String url = getStreamingOriginLocator(encodedAsset);
System.out.println("Origin Locator URL: " + url);
System.out.println("Sample completed!");
} catch (ServiceException se) {
System.out.println("ServiceException encountered.");
System.out.println(se.toString());
} catch (Exception e) {
System.out.println("Exception encountered.");
System.out.println(e.toString());
}
}
private static AssetInfo uploadFileAndCreateAsset(String fileName)
throws ServiceException, FileNotFoundException, NoSuchAlgorithmException {
WritableBlobContainerContract uploader;
AssetInfo resultAsset;
AccessPolicyInfo uploadAccessPolicy;
LocatorInfo uploadLocator = null;
// Create an Asset
resultAsset = mediaService.create(Asset.create().setName(fileName).setAlternateId("altId"));
System.out.println("Created Asset " + fileName);
// Create an AccessPolicy that provides Write access for 15 minutes
uploadAccessPolicy = mediaService
.create(AccessPolicy.create("uploadAccessPolicy", 15.0, EnumSet.of(AccessPolicyPermission.WRITE)));
// Create a Locator using the AccessPolicy and Asset
uploadLocator = mediaService
.create(Locator.create(uploadAccessPolicy.getId(), resultAsset.getId(), LocatorType.SAS));
// Create the Blob Writer using the Locator
uploader = mediaService.createBlobWriter(uploadLocator);
File file = new File("BigBuckBunny.mp4");
// The local file that will be uploaded to your Media Services account
InputStream input = new FileInputStream(file);
System.out.println("Uploading " + fileName);
// Upload the local file to the asset
uploader.createBlockBlob(fileName, input);
// Inform Media Services about the uploaded files
mediaService.action(AssetFile.createFileInfos(resultAsset.getId()));
System.out.println("Uploaded Asset File " + fileName);
mediaService.delete(Locator.delete(uploadLocator.getId()));
mediaService.delete(AccessPolicy.delete(uploadAccessPolicy.getId()));
return resultAsset;
}
// Create a Job that contains a Task to transform the Asset
private static AssetInfo encode(AssetInfo assetToEncode)
throws ServiceException, InterruptedException {
// Retrieve the list of Media Processors that match the name
ListResult<MediaProcessorInfo> mediaProcessors = mediaService
.list(MediaProcessor.list().set("$filter", String.format("Name eq '%s'", preferedEncoder)));
// Use the latest version of the Media Processor
MediaProcessorInfo mediaProcessor = null;
for (MediaProcessorInfo info : mediaProcessors) {
if (null == mediaProcessor || info.getVersion().compareTo(mediaProcessor.getVersion()) > 0) {
mediaProcessor = info;
}
}
System.out.println("Using Media Processor: " + mediaProcessor.getName() + " " + mediaProcessor.getVersion());
// Create a task with the specified Media Processor
String outputAssetName = String.format("%s as %s", assetToEncode.getName(), encodingPreset);
String taskXml = "<taskBody><inputAsset>JobInputAsset(0)</inputAsset>"
+ "<outputAsset assetCreationOptions=\"0\"" // AssetCreationOptions.None
+ " assetName=\"" + outputAssetName + "\">JobOutputAsset(0)</outputAsset></taskBody>";
Task.CreateBatchOperation task = Task.create(mediaProcessor.getId(), taskXml)
.setConfiguration(encodingPreset).setName("Encoding");
// Create the Job; this automatically schedules and runs it.
Job.Creator jobCreator = Job.create()
.setName(String.format("Encoding %s to %s", assetToEncode.getName(), encodingPreset))
.addInputMediaAsset(assetToEncode.getId()).setPriority(2).addTaskCreator(task);
JobInfo job = mediaService.create(jobCreator);
String jobId = job.getId();
System.out.println("Created Job with Id: " + jobId);
// Check to see if the Job has completed
checkJobStatus(jobId);
// Done with the Job
// Retrieve the output Asset
ListResult<AssetInfo> outputAssets = mediaService.list(Asset.list(job.getOutputAssetsLink()));
return outputAssets.get(0);
}
public static String getStreamingOriginLocator(AssetInfo asset) throws ServiceException {
// Get the .ISM AssetFile
ListResult<AssetFileInfo> assetFiles = mediaService.list(AssetFile.list(asset.getAssetFilesLink()));
AssetFileInfo streamingAssetFile = null;
for (AssetFileInfo file : assetFiles) {
if (file.getName().toLowerCase().endsWith(".ism")) {
streamingAssetFile = file;
break;
}
}
AccessPolicyInfo originAccessPolicy;
LocatorInfo originLocator = null;
// Create a 30-day readonly AccessPolicy
double durationInMinutes = 60 * 24 * 30;
originAccessPolicy = mediaService.create(
AccessPolicy.create("Streaming policy", durationInMinutes, EnumSet.of(AccessPolicyPermission.READ)));
// Create a Locator using the AccessPolicy and Asset
originLocator = mediaService
.create(Locator.create(originAccessPolicy.getId(), asset.getId(), LocatorType.OnDemandOrigin));
// Create a Smooth Streaming base URL
return originLocator.getPath() + streamingAssetFile.getName() + "/manifest";
}
private static void checkJobStatus(String jobId) throws InterruptedException, ServiceException {
boolean done = false;
JobState jobState = null;
while (!done) {
// Sleep for 5 seconds
Thread.sleep(5000);
// Query the updated Job state
jobState = mediaService.get(Job.get(jobId)).getState();
System.out.println("Job state: " + jobState);
if (jobState == JobState.Finished || jobState == JobState.Canceled || jobState == JobState.Error) {
done = true;
}
}
}
}
I verified following code below which is working through fiddler proxy. Thanks to how to Capture https with fiddler, in java post which gave me hints:
System.setProperty("http.proxyHost", "127.0.0.1");
System.setProperty("https.proxyHost", "127.0.0.1");
System.setProperty("http.proxyPort", "8888");
System.setProperty("https.proxyPort", "8888");
System.setProperty("javax.net.ssl.trustStore", "C:\\Program Files\\Java\\jdk1.8.0_102\\bin\\FiddlerKeyStore");
System.setProperty("javax.net.ssl.trustStorePassword", "mypassword");
For others who face issue like me we can connect to azure mediaservices using network proxy by using below code
// Set up the MediaContract object to call into the Media Services account
Configuration configuration = MediaConfiguration.configureWithOAuthAuthentication(
mediaServiceUri, oAuthUri, clientId, clientSecret, scope);
configuration.getProperties().put(Configuration.PROPERTY_HTTP_PROXY_HOST, "Hostvalue");
configuration.getProperties().put(Configuration.PROPERTY_HTTP_PROXY_PORT, "Portvalue");
configuration.getProperties().put(Configuration.PROPERTY_HTTP_PROXY_SCHEME, "http");
MediaContract mediaService = MediaService.create(configuration);
Now use the mediaService to perform other operations.
Tried the following:
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.spi.FileTypeDetector;
import org.apache.tika.Tika;
import org.apache.tika.mime.MimeTypes;
/**
*
* #author kiriti.k
*/
public class TikaFileTypeDetector {
private final Tika tika = new Tika();
public TikaFileTypeDetector() {
super();
}
public String probeContentType(Path path) throws IOException {
// Try to detect based on the file name only for efficiency
String fileNameDetect = tika.detect(path.toString());
if (!fileNameDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileNameDetect;
}
// Then check the file content if necessary
String fileContentDetect = tika.detect(path.toFile());
if (!fileContentDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileContentDetect;
}
// Specification says to return null if we could not
// conclusively determine the file type
return null;
}
public static void main(String[] args) throws IOException {
Tika tika = new Tika();
// expects file path as the program argument
if (args.length != 1) {
printUsage();
return;
}
Path path = Paths.get(args[0]);
TikaFileTypeDetector detector = new TikaFileTypeDetector();
// Analyse the file - first based on file name for efficiency.
// If cannot determine based on name and then analyse content
String contentType = detector.probeContentType(path);
System.out.println("File is of type - " + contentType);
}
public static void printUsage() {
System.out.print("Usage: java -classpath ... "
+ TikaFileTypeDetector.class.getName()
+ " ");
}
}
The above program is checking based on file extension only. How do I make it to check content type also(mime) and then determine the type. I am using tika-app-1.8.jar in netbean 8.0.2. What am I missing?
The code checks the file extension first and returns the MIME type based on that, if it finds a result. If you want it to check the content first, just switch the two statements:
public String probeContentType(Path path) throws IOException {
// Check contents first
String fileContentDetect = tika.detect(path.toFile());
if (!fileContentDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileContentDetect;
}
// Try file name only if content search was not successful
String fileNameDetect = tika.detect(path.toString());
if (!fileNameDetect.equals(MimeTypes.OCTET_STREAM)) {
return fileNameDetect;
}
// Specification says to return null if we could not
// conclusively determine the file type
return null;
}
Be aware that this may have huge performance impact.
You can use Files.probeContentType(path)
I am learning java servlet programming and I wrote a program to upload files, I'm having a strange problem with the program. when it says it finished uploading the file and when i click the link to see it I get a 404 error and when i check the directory (where the file is supposed to be saved ) its empty. I checked the log and there are no error messages.I got the code from a book I'm using to learn servlet and jsp.
here is my java code
import java.io.*;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class FileUpload
*/
#WebServlet("/FileUpload")
public class FileUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public FileUpload() {
super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.print("File upload success. <a href=\"/Project_One/files");
out.print("\">Click here to browse through all uploaded ");
out.println("files.</a><br>");
ServletInputStream sis = request.getInputStream();
StringWriter sw = new StringWriter();
int i = sis.read();
for(;i!=-1 && i!= '\r';i=sis.read())
{
sw.write(i);
}
sis.read(); //ditch \'n'
String delimiter = sw.toString();
while(true)
{
StringWriter h = new StringWriter();
int[] temp = new int[4];
temp[0] = (byte)sis.read();
temp[1] = (byte)sis.read();
temp[2] = (byte)sis.read();
h.write(temp[0]);
h.write(temp[1]);
h.write(temp[2]);
//read header
for(temp[3]=sis.read();temp[3]!=-1;temp[3]=sis.read())
{
if(temp[0] == '\r' &&
temp[1] == '\n' &&
temp[2] == 'r' &&
temp[3] == '\n')
{
break;
}
h.write(temp[3]);
temp[0]= temp[1];
temp[1]= temp[2];
temp[2]= temp[3];
}
String header = h.toString();
int StartName = header.indexOf("name=\"");
int endName = header.indexOf("\"",StartName+6);
if(StartName == -1|| endName == -1)
{
break;
}
String name = header.substring(StartName+6,endName);
if(name.equals("file"))
{
StartName = header.indexOf("filename=\"");
endName = header.indexOf("\"",StartName+10);
String filename = header.substring(StartName+10,endName);
ServletConfig config = getServletConfig();
ServletContext sc = config.getServletContext();
//File file = new File(sc.getRealPath("/files"));
//file.mkdirs();
FileOutputStream fos = new FileOutputStream(sc.getRealPath("/")+"/"+filename);
//write the file to disk
int length = delimiter.length();
//delimiter ="\r\n"+delimiter;
byte[] body = new byte[delimiter.length()];
for(int j=0;j<body.length-1;j++)
{
body[j]=(byte)sis.read();
fos.write(body[j]);
}
//check it wasn't a 0 length file
//if(!delimiter.equals(new String (body)))
//{
int e = body.length-1;
i=sis.read();
for(;i!=-1;i=sis.read())
{
body[e]=(byte)i;
/*fos.write(body[0]);
for(int l=0;l<body.length-1;l++)
{
body[l]=body[l+1];
}*/
//body[e]=(byte)i;
if(delimiter.equals(new String (body)))
{
break;
}
//length++;
fos.write(body[e]);
for(int k=0;k<body.length-1;k++)
{
body[k]=body[k+1];
}
length++;
}
fos.flush();
fos.close();
out.println("<p><b>Saved File:</b>"+filename+"</p>");
out.println("<p><b>Length:</b>"+ length+"</p>");
}
if(sis.read() == '-' && sis.read()=='-')
{
break;
}
}
out.println("</html>");
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
There were few changes made in the code , the changes were given in the book.
here is my HTML code
<html>
<head>
<title>Test HTML Form</title>
</head>
<body>
<p>Select a file to upload or browse currently uploaded files.</p>
<form action="http://127.0.0.1/Project_One/FileUpload"
method="post" enctype="multipart/form-data">
File:<input type="file" name:"file"><br>
<input value="Upload File" type="submit">
</form>
</body>
</html>
I'm using TomCat sever for this.
Where did you get this code from? From a decade old servlet tutorial/book? This is all unnecessarily overcomplicated. Please make sure that you're reading an up to date tutorial/book which is no older than one year.
Here's how the file upload could be done with the standard servlet 3.0 API:
#MultipartConfig
#WebServlet("/FileUpload")
public class FileUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
String filename = getFilename(filePart);
InputStream filecontent = filePart.getInputStream();
// ... (do your job here)
}
private static String getFilename(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String filename = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return filename.substring(filename.lastIndexOf('/') + 1).substring(filename.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
}
That's all. It also takes into account that the proper filename is returned. Some browsers such as MSIE namely incorrectly includes the full client side path along the filename. That might be the cause of your problem.
Further there are 2 other problems not directly related:
You should not store the uploaded file in the deploy folder. It will get lost whenever you redeploy the webapp. Store the file in a fixed path somewhere outside the deploy folder. See also for example How I save and retrieve an image on my server in a java webapp.
You should be delegating the job of generating HTML to JSP. In the end of doPost(), forward the request to a JSP:
request.getRequestDispatcher("/WEB-INF/uploadresult.jsp").forward(request, response);
See also:
Our Servlets wiki page - contains hello world examples how to use servlets properly
How to upload files to server using JSP/Servlet?
well i think this code is a bit complicated to read but there are a few points where the error could be, first of all this part :
out.println("<html>");
out.print("File upload success. <a href=\"/Project_One/files");
out.print("\">Click here to browse through all uploaded ");
out.println("files.</a><br>");
In this part your link points to Project_One/files but when you write your file :
FileOutputStream fos = new FileOutputStream(sc.getRealPath("/")+"/"+filename);
you write the file directly in the Project_One folder ( not in the files folder your html points ) , so you could try to see if the file was written in the main folder of your workspace.
Anyway i think you could understand better a code like this :
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
MultipartFile multipartFile = multipartRequest.getFile("file");
byte[] content =multipartFile.getBytes();
File archivoParaGuardar= new File("/your_directory/"+multipartFile.getOriginalFilename());
try {
baos.write(content);
FileOutputStream fos = new FileOutputStream(archivoParaGuardar);
baos.writeTo(fos);
fos.close();
} catch (Exception e) {
logger.error("Error saving file ", e);
}
Hope this helps you.
I have this code which reads all the files from a directory.
File textFolder = new File("text_directory");
File [] texFiles = textFolder.listFiles( new FileFilter() {
public boolean accept( File file ) {
return file.getName().endsWith(".txt");
}
});
It works great. It fills the array with all the files that end with ".txt" from directory "text_directory".
How can I read the contents of a directory in a similar fashion within a JAR file?
So what I really want to do is, to list all the images inside my JAR file, so I can load them with:
ImageIO.read(this.getClass().getResource("CompanyLogo.png"));
(That one works because the "CompanyLogo" is "hardcoded" but the number of images inside the JAR file could be from 10 to 200 variable length.)
EDIT
So I guess my main problem would be: How to know the name of the JAR file where my main class lives?
Granted I could read it using java.util.Zip.
My Structure is like this:
They are like:
my.jar!/Main.class
my.jar!/Aux.class
my.jar!/Other.class
my.jar!/images/image01.png
my.jar!/images/image02a.png
my.jar!/images/imwge034.png
my.jar!/images/imagAe01q.png
my.jar!/META-INF/manifest
Right now I'm able to load for instance "images/image01.png" using:
ImageIO.read(this.getClass().getResource("images/image01.png));
But only because I know the file name, for the rest I have to load them dynamically.
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
while(true) {
ZipEntry e = zip.getNextEntry();
if (e == null)
break;
String name = e.getName();
if (name.startsWith("path/to/your/dir/")) {
/* Do something with this entry. */
...
}
}
}
else {
/* Fail... */
}
Note that in Java 7, you can create a FileSystem from the JAR (zip) file, and then use NIO's directory walking and filtering mechanisms to search through it. This would make it easier to write code that handles JARs and "exploded" directories.
Code that works for both IDE's and .jar files:
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class ResourceWalker {
public static void main(String[] args) throws URISyntaxException, IOException {
URI uri = ResourceWalker.class.getResource("/resources").toURI();
Path myPath;
if (uri.getScheme().equals("jar")) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
myPath = fileSystem.getPath("/resources");
} else {
myPath = Paths.get(uri);
}
Stream<Path> walk = Files.walk(myPath, 1);
for (Iterator<Path> it = walk.iterator(); it.hasNext();){
System.out.println(it.next());
}
}
}
erickson's answer worked perfectly:
Here's the working code.
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();
if( src != null ) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream( jar.openStream());
ZipEntry ze = null;
while( ( ze = zip.getNextEntry() ) != null ) {
String entryName = ze.getName();
if( entryName.startsWith("images") && entryName.endsWith(".png") ) {
list.add( entryName );
}
}
}
webimages = list.toArray( new String[ list.size() ] );
And I have just modify my load method from this:
File[] webimages = ...
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));
To this:
String [] webimages = ...
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
I would like to expand on acheron55's answer, since it is a very non-safe solution, for several reasons:
It doesn't close the FileSystem object.
It doesn't check if the FileSystem object already exists.
It isn't thread-safe.
This is somewhat a safer solution:
private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();
public void walk(String path) throws Exception {
URI uri = getClass().getResource(path).toURI();
if ("jar".equals(uri.getScheme()) {
safeWalkJar(path, uri);
} else {
Files.walk(Paths.get(path));
}
}
private void safeWalkJar(String path, URI uri) throws Exception {
synchronized (getLock(uri)) {
// this'll close the FileSystem object at the end
try (FileSystem fs = getFileSystem(uri)) {
Files.walk(fs.getPath(path));
}
}
}
private Object getLock(URI uri) {
String fileName = parseFileName(uri);
locks.computeIfAbsent(fileName, s -> new Object());
return locks.get(fileName);
}
private String parseFileName(URI uri) {
String schemeSpecificPart = uri.getSchemeSpecificPart();
return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!"));
}
private FileSystem getFileSystem(URI uri) throws IOException {
try {
return FileSystems.getFileSystem(uri);
} catch (FileSystemNotFoundException e) {
return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap());
}
}
There's no real need to synchronize over the file name; one could simply synchronize on the same object every time (or make the method synchronized), it's purely an optimization.
I would say that this is still a problematic solution, since there might be other parts in the code that use the FileSystem interface over the same files, and it could interfere with them (even in a single threaded application).
Also, it doesn't check for nulls (for instance, on getClass().getResource().
This particular Java NIO interface is kind of horrible, since it introduces a global/singleton non thread-safe resource, and its documentation is extremely vague (a lot of unknowns due to provider specific implementations). Results may vary for other FileSystem providers (not JAR). Maybe there's a good reason for it being that way; I don't know, I haven't researched the implementations.
So I guess my main problem would be, how to know the name of the jar where my main class lives.
Assuming that your project is packed in a Jar (not necessarily true!), you can use ClassLoader.getResource() or findResource() with the class name (followed by .class) to get the jar that contains a given class. You'll have to parse the jar name from the URL that gets returned (not that tough), which I will leave as an exercise for the reader :-)
Be sure to test for the case where the class is not part of a jar.
I've ported acheron55's answer to Java 7 and closed the FileSystem object. This code works in IDE's, in jar files and in a jar inside a war on Tomcat 7; but note that it does not work in a jar inside a war on JBoss 7 (it gives FileSystemNotFoundException: Provider "vfs" not installed, see also this post). Furthermore, like the original code, it is not thread safe, as suggested by errr. For these reasons I have abandoned this solution; however, if you can accept these issues, here is my ready-made code:
import java.io.IOException;
import java.net.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
public class ResourceWalker {
public static void main(String[] args) throws URISyntaxException, IOException {
URI uri = ResourceWalker.class.getResource("/resources").toURI();
System.out.println("Starting from: " + uri);
try (FileSystem fileSystem = (uri.getScheme().equals("jar") ? FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()) : null)) {
Path myPath = Paths.get(uri);
Files.walkFileTree(myPath, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
return FileVisitResult.CONTINUE;
}
});
}
}
}
Here is an example of using Reflections library to recursively scan classpath by regex name pattern augmented with a couple of Guava perks to to fetch resources contents:
Reflections reflections = new Reflections("com.example.package", new ResourcesScanner());
Set<String> paths = reflections.getResources(Pattern.compile(".*\\.template$"));
Map<String, String> templates = new LinkedHashMap<>();
for (String path : paths) {
log.info("Found " + path);
String templateName = Files.getNameWithoutExtension(path);
URL resource = getClass().getClassLoader().getResource(path);
String text = Resources.toString(resource, StandardCharsets.UTF_8);
templates.put(templateName, text);
}
This works with both jars and exploded classes.
Here's a method I wrote for a "run all JUnits under a package". You should be able to adapt it to your needs.
private static void findClassesInJar(List<String> classFiles, String path) throws IOException {
final String[] parts = path.split("\\Q.jar\\\\E");
if (parts.length == 2) {
String jarFilename = parts[0] + ".jar";
String relativePath = parts[1].replace(File.separatorChar, '/');
JarFile jarFile = new JarFile(jarFilename);
final Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
final String entryName = entry.getName();
if (entryName.startsWith(relativePath)) {
classFiles.add(entryName.replace('/', File.separatorChar));
}
}
}
}
Edit:
Ah, in that case, you might want this snippet as well (same use case :) )
private static File findClassesDir(Class<?> clazz) {
try {
String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
final String codeSourcePath = URLDecoder.decode(path, "UTF-8");
final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce('.', File.separatorChar));
} catch (UnsupportedEncodingException e) {
throw new AssertionError("impossible", e);
}
}
Just to mention that if you are already using Spring, you can take advantage of the PathMatchingResourcePatternResolver.
For instance to get all the PNG files from a images folder in resources
ClassLoader cl = this.getClass().getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
Resource[] resources = resolver.getResources("images/*.png");
for (Resource r: resources){
logger.info(r.getFilename());
// From your example
// ImageIO.read(cl.getResource("images/" + r.getFilename()));
}
A jar file is just a zip file with a structured manifest. You can open the jar file with the usual java zip tools and scan the file contents that way, inflate streams, etc. Then use that in a getResourceAsStream call, and it should be all hunky dory.
EDIT / after clarification
It took me a minute to remember all the bits and pieces and I'm sure there are cleaner ways to do it, but I wanted to see that I wasn't crazy. In my project image.jpg is a file in some part of the main jar file. I get the class loader of the main class (SomeClass is the entry point) and use it to discover the image.jpg resource. Then some stream magic to get it into this ImageInputStream thing and everything is fine.
InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg");
JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi();
ImageReader ir = imageReaderSpi.createReaderInstance();
ImageInputStream iis = new MemoryCacheImageInputStream(inputStream);
ir.setInput(iis);
....
ir.read(0); //will hand us a buffered image
Given an actual JAR file, you can list the contents using JarFile.entries(). You will need to know the location of the JAR file though - you can't just ask the classloader to list everything it could get at.
You should be able to work out the location of the JAR file based on the URL returned from ThisClassName.class.getResource("ThisClassName.class"), but it may be a tiny bit fiddly.
Some time ago I made a function that gets classess from inside JAR:
public static Class[] getClasses(String packageName)
throws ClassNotFoundException{
ArrayList<Class> classes = new ArrayList<Class> ();
packageName = packageName.replaceAll("\\." , "/");
File f = new File(jarName);
if(f.exists()){
try{
JarInputStream jarFile = new JarInputStream(
new FileInputStream (jarName));
JarEntry jarEntry;
while(true) {
jarEntry=jarFile.getNextJarEntry ();
if(jarEntry == null){
break;
}
if((jarEntry.getName ().startsWith (packageName)) &&
(jarEntry.getName ().endsWith (".class")) ) {
classes.add(Class.forName(jarEntry.getName().
replaceAll("/", "\\.").
substring(0, jarEntry.getName().length() - 6)));
}
}
}
catch( Exception e){
e.printStackTrace ();
}
Class[] classesA = new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}else
return null;
}
public static ArrayList<String> listItems(String path) throws Exception{
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
byte[] b = new byte[in.available()];
in.read(b);
String data = new String(b);
String[] s = data.split("\n");
List<String> a = Arrays.asList(s);
ArrayList<String> m = new ArrayList<>(a);
return m;
}
There are two very useful utilities both called JarScan:
www.inetfeedback.com/jarscan
jarscan.dev.java.net
See also this question: JarScan, scan all JAR files in all subfolders for specific class
The most robust mechanism for listing all resources in the classpath is currently to use this pattern with ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author of ClassGraph.)
How to know the name of the JAR file where my main class lives?
URI mainClasspathElementURI;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
.enableClassInfo().scan()) {
mainClasspathElementURI =
scanResult.getClassInfo("x.y.z.MainClass").getClasspathElementURI();
}
How can I read the contents of a directory in a similar fashion within a JAR file?
List<String> classpathElementResourcePaths;
try (ScanResult scanResult = new ClassGraph().overrideClasspath(mainClasspathElementURI)
.scan()) {
classpathElementResourcePaths = scanResult.getAllResources().getPaths();
}
There are lots of other ways to deal with resources too.
One more for the road that's a bit more flexible for matching specific filenames because it uses wildcard globbing. In a functional style this could resemble:
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import static java.nio.file.FileSystems.getDefault;
import static java.nio.file.FileSystems.newFileSystem;
import static java.util.Collections.emptyMap;
/**
* Responsible for finding file resources.
*/
public class ResourceWalker {
/**
* Globbing pattern to match font names.
*/
public static final String GLOB_FONTS = "**.{ttf,otf}";
/**
* #param directory The root directory to scan for files matching the glob.
* #param c The consumer function to call for each matching path
* found.
* #throws URISyntaxException Could not convert the resource to a URI.
* #throws IOException Could not walk the tree.
*/
public static void walk(
final String directory, final String glob, final Consumer<Path> c )
throws URISyntaxException, IOException {
final var resource = ResourceWalker.class.getResource( directory );
final var matcher = getDefault().getPathMatcher( "glob:" + glob );
if( resource != null ) {
final var uri = resource.toURI();
final Path path;
FileSystem fs = null;
if( "jar".equals( uri.getScheme() ) ) {
fs = newFileSystem( uri, emptyMap() );
path = fs.getPath( directory );
}
else {
path = Paths.get( uri );
}
try( final var walk = Files.walk( path, 10 ) ) {
for( final var it = walk.iterator(); it.hasNext(); ) {
final Path p = it.next();
if( matcher.matches( p ) ) {
c.accept( p );
}
}
} finally {
if( fs != null ) { fs.close(); }
}
}
}
}
Consider parameterizing the file extensions, left an exercise for the reader.
Be careful with Files.walk. According to the documentation:
This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directories are closed promptly after the stream's operations have completed.
Likewise, newFileSystem must be closed, but not before the walker has had a chance to visit the file system paths.
Just a different way of listing/reading files from a jar URL and it does it recursively for nested jars
https://gist.github.com/trung/2cd90faab7f75b3bcbaa
URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo");
JarReader.read(urlResource, new InputStreamCallback() {
#Override
public void onFile(String name, InputStream is) throws IOException {
// got file name and content stream
}
});