How can I use JXBrowser to upload whole Directory, not just multiple files - jxbrowser

Chrome supported the non-standard 'webkitdirectory' property for html 'input' element since 2011, and it works great. codepen sample
I would like to run jxbrower. Its latest version as of Mon, 21 May, 2018 was based on Chromium 64.0.3282.24 (Dec 12, 2017), yet it does not support this property.
What am I missing ? Any suggestions would be greatly appreciated.
<html>
<div>
<label for="uploaddir" style="width: 600px; background: #ccc;border: 1px solid black;">Choose directory to upload</label>
<input type="file" id="uploaddir" webkitdirectory onchange="updateImageDisplay()" style="opacity:0;">
</div>
<div>
<ul id="listing"></ul>
</div>
</html>
<script>
var input = document.getElementById('uploaddir');
var output = document.getElementById('listing');
function updateImageDisplay() {
while (output.firstChild) {
output.removeChild(output.firstChild);
}
var curFiles = input.files;
for (let i = 0; i < curFiles.length; i++) {
const item = document.createElement('li');
item.innerHTML = `${curFiles[i].webkitRelativePath} (${curFiles[i].size} bytes)`;
output.appendChild(item);
}
}
</script>

JxBrowser can upload the whole directory. What can be wrong in your case is the file chooser dialog that is not configured to chose directories.
I make a simple example to demonstrate how you could configure file chooser dialog in swing:
browser.setDialogHandler(new DefaultDialogHandler(view) {
#Override
public CloseStatus onFileChooser(FileChooserParams params) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
if (fileChooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
params.setSelectedFiles(selectedFile.getAbsolutePath());
return CloseStatus.OK;
}
return CloseStatus.CANCEL;
}
});

#Dmitry. This is the javafx implementation analogous to this swing example. Note the following two crucial observations.
Need 'Runnable runnable' or it throws this error: 'SEVERE: The DialogHandler.onFileChooser() method has thrown exception:
java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Browser Events Thread'.
Need 'FutureTask' to BLOCK the Event(?) thread until the user clicks OK in the directory browser. Otherwise, onFileChooser will return CloseStatus.CANCEL immediately. By the time params.setSelectedFiles is called , it's already too late, and any uploaded files will not be received by the angular controller.
private String path = "C:\\Users\\user\\Desktop\\temp\\foo";
private void setDirectoryListener(Stage primaryStage) {
browser.setDialogHandler(new DefaultDialogHandler(view) {
#Override
public CloseStatus onFileChooser(FileChooserParams params) {
final AtomicReference<CloseStatus> status = new AtomicReference<>(
CloseStatus.CANCEL);
Runnable runnable = () -> {
if (params.getMode() == FileChooserMode.OpenFolder) {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Open Resource Folder");
directoryChooser.setInitialDirectory(new File(path));
File selectedDirectory = directoryChooser.showDialog(primaryStage);
if (selectedDirectory != null) { // in case of CANCEL
List<File> allFiles = new ArrayList<>();
getOnlyFiles(selectedDirectory, allFiles);
params.setSelectedFiles(allFiles.toArray(new File[0]));
status.set(CloseStatus.OK);
}
}
};
FutureTask<Void> task = new FutureTask<>(runnable, null);
Platform.runLater(task);
try {
task.get();
}
catch (InterruptedException interrupt) {
throw new Error("Unexpected interruption");
}
catch (ExecutionException exc) {
throw new RuntimeException(exc);
}
return status.get();
}
});
}
private static void getOnlyFiles(File file, List<File> files {
if (file.isFile()) {
System.out.println(file.getAbsolutePath());
files.add(file);
}
File[] children = file.listFiles();
if (children == null)
return;
for (File child : children) {
getOnlyFiles(child, files);
}
}
INPUT:
`<input id="dirinput1" type="file" webkitdirectory ngf-select="uploadDir($files)/>`
OUTPUT:
C:\Users\user\Desktop\temp\foo\bar\csv.png
C:\Users\user\Desktop\temp\foo\bar\import_OQ_Manual.txt
C:\Users\user\Desktop\temp\foo\mz.PNG
C:\Users\user\Desktop\temp\foo\test.txt

Related

How to enable Platform.runlater within method

I am creating a Javafx chat app which also allows for file transfer. My issue is I open a FileOutputStream for the received file within the below method. I can see my listener.statusTransferring() updating the UI only if I enable Platform.runLater. I think I now need to enable the same on the fos.write(b, 0, tmpTransferred) within the while loop but don't know how to do this. I have tried unsuccessfully wrapping the whole method within Platform runlater. Note: If I don't use platform runlater I don't get any errors however the UI does not update until the file transfer is complete eg listener.statusCompleted() is called;. The error I get now as a result of the fos being in Platform runlater. is below.. Line 185 is fos.write(b, 0, tmpTransferred); The other listener calls appear to work fine. Just not listener.statusTransferring(); or listener.transferUpdate(); which utilise the fos. Any help will be greatly appreciated. Also for your own sanity I am a self taught google programmer. Yep the worst kind I am sure. Thanks in advance.
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at net.thebowdens.net.FileReceiver.transfer(FileReceiver.java:185)
at net.thebowdens.net.DefaultMessageResponder.fileSend(DefaultMessageResponder.java:543)
public boolean transfer() {
listener.statusConnecting();
received = false;
cancel = false;
try {
if (sSock != null) {
sock = sSock.accept();
listener.statusTransferring();
Platform.runLater(() ->{
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
is = sock.getInputStream();
final byte[] b = new byte[1024];
transferred = 0;
percent = 0;
int tmpTransferred = 0;
int tmpPercent = 0;
int transCounter = 0;
bCounter.prepare();
while (!cancel && (tmpTransferred = is.read(b)) != -1) {
fos.write(b, 0, tmpTransferred);
transferred += tmpTransferred;
percent = (int) ((transferred * 100) / size);
bCounter.addBytes(tmpTransferred);
transCounter++;
if (percent > tmpPercent || transCounter >= 250) {
transCounter = 0;
tmpPercent = percent;
listener.transferUpdate();
}
}
if (!cancel && transferred == size) {
received = true;
listener.statusCompleted();
}
else {
listener.statusFailed();
}
}
}
catch (final IOException e) {
LOG.log(Level.SEVERE, e.toString());
listener.statusFailed();
}
finally {
stopReceiver();
cleanupConnections();
}
return received;
}
Keep in mind that you should use Platform.runLater only for updating the UI, everything else should be outside it otherwhise the UI will become unresponsive.
I suggest you to to refactor your code according to this.
Well after much discussion over the correct language and other issues I solved my problem of the UI updating. I had two issues. My choice selector and Filechooser methods were not on the Javafx application thread (hope this is the right terminology) so I had to do the following:
private ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
#Override
public void run() {
Platform.runLater(() -> {
try {
receiveRequest(tmpUser, fileRes, user, fileName, size, fileHash);
} catch (IOException | ServerException | CommandException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
});
I then had to do the same within the Filechooser and file accept method for the transfer class UI to update
executorService.execute(new Runnable() {
#Override public void run() {
if (fileRes.transfer()) {
ui.showSystemMessage("Successfully received " + fileName +
" from " + user + ", and saved as " + fileRes.getFile().getName());
}
else {
ui.showSystemMessage("Failed to receive " + fileName + " from " + user);
fileRes.cancel();
}
}
});
}

javafx bind button disable property to folder exists

I have a stage with a button "Continue" which is want to be disabled until a folder "folderFramework" is created.
Here is my snippet which unfortunately does not work.
File folderFramework = new File("path to the folder");
ObjectProperty<File> file = new SimpleObjectProperty<>();
file.set(folderFramework); // tested with setValue too...
BooleanBinding fileExists = Bindings.createBooleanBinding(() ->
file.get() != null && file.get().exists(),
file);
btnContinue.disableProperty().bind(fileExists.not());
What am I missing? Thank you!
WatchService in the Task did the trick for me. Here is the final solution:
private void handleBtnContinue() {
// task definition for watching changes in framework folder
Task<Void> watchingTask = new Task<>() {
#Override
protected Void call() {
WatchService watchService;
try {
watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get(ConstantUtils.getPathFolderFramework());
path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
boolean quitTask = false;
while ((key = watchService.take()) != null && !quitTask) {
for (WatchEvent<?> event : key.pollEvents()) {
// when src folder is cloned -> enable the button continue
if (event.kind().toString().equals("ENTRY_MODIFY") && event.context().toString().equals("src")) {
// enable btn continue
Platform.runLater(() -> btnContinue.setDisable(false));
// end the task
quitTask = true;
}
}
key.reset();
}
} catch (IOException | InterruptedException e) {
LOG.error("Error occurred while trying to watch changes in folder: " + ConstantUtils.getPathFolderAppData());
}
return null;
}
};
// run the task in a new thread
Thread th = new Thread(watchingTask);
th.setDaemon(true);
th.start();
}

Codename One: Connecting and populating a drop-down menu with an SQLite database

I am trying to connect an SQLite database file to a picker component (accepting strings). This should act similar to a drop-down menu. I have tried to follow previous advice and examples, but without success.
As indicated in a previous post, I have saved the database file in the source folder of the application. View of the source folder where I have saved the database file (highlighted).
The code I have used to implement my app is as follows with the below layout.
//-----------------------
database code
//-----------------------
public class MyApplication {
private Form current;
private Resources theme;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Pro only feature, uncomment if you have a pro subscription
// Log.bindCrashProtection(true);
}
private Container Home() {
Container home = new Container(new BoxLayout(BoxLayout.Y_AXIS));
return home;
}
private Container AddItem() {
Container addItem = new Container(new BoxLayout(BoxLayout.Y_AXIS));
TextArea item = new TextArea("Add Item");
addItem.addComponent(item);
Picker selectItem = new Picker();
selectItem.setType(Display.PICKER_TYPE_STRINGS);
//----------------------------------------------------------------------------------
Database db = null;
Cursor cur = null;
try {
db = Display.getInstance().openOrCreate("FoodAndBeverage.db");
if(selectItem.getText().startsWith("Still Water")) {
cur = db.executeQuery(selectItem.getText());
int columns = cur.getColumnCount();
addItem.removeAll();
if(columns > 0) {
boolean next = cur.next();
if(next) {
ArrayList<String[]> data = new ArrayList<>();
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = cur.getColumnName(iter);
}
while(next) {
Row currentRow = cur.getRow();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = currentRow.getString(iter);
}
data.add(currentRowArray);
next = cur.next();
}
Object[][] arr = new Object[data.size()][];
data.toArray(arr);
addItem.add(BorderLayout.CENTER, new Table(new DefaultTableModel(columnNames, arr)));
} else {
addItem.add(BorderLayout.CENTER, "Query returned no results");
}
} else {
addItem.add(BorderLayout.CENTER, "Query returned no results");
}
} else {
db.execute(selectItem.getText());
addItem.add(BorderLayout.CENTER, "Query completed successfully");
}
addItem.revalidate();
} catch(IOException err) {
Log.e(err);
addItem.removeAll();
addItem.add(BorderLayout.CENTER, "Error: " + err);
addItem.revalidate();
} finally {
Util.cleanup(db);
Util.cleanup(cur);
}
//---------------------------------------------------------------------------------------------
addItem.addComponent(selectItem);
TextField quantity = new TextField("", "Quantity (ml or g)", 4, TextArea.NUMERIC);
addItem.addComponent(quantity);
Button add = new Button("Add");
addItem.addComponent(add);
TextArea results = new TextArea("Results");
addItem.addComponent(results);
return addItem;
}
private Container Settings() {
Container settings = new Container(new BoxLayout(BoxLayout.Y_AXIS));
TextArea nutrients = new TextArea("Target");
settings.addComponent(nutrients);
TextField volume = new TextField("", "Volume (ml)", 4, TextArea.NUMERIC);
settings.addComponent(volume);
TextArea duration = new TextArea("Hydration Duration");
settings.addComponent(duration);
settings.add("Start:");
Picker start = new Picker();
start.setType(Display.PICKER_TYPE_TIME);
settings.addComponent(start);
settings.add("End:");
Picker end = new Picker();
end.setType(Display.PICKER_TYPE_TIME);
settings.addComponent(end);
Button save = new Button("Save");
settings.addComponent(save);
return settings;
}
public void start() {
if(current != null)
{
current.show();
return;
}
Form home = new Form("Hydrate", new BorderLayout());
Tabs t = new Tabs();
t.addTab("Home", Home());
t.addTab("Intake", AddItem());
t.addTab("Settings", Settings());
home.add(BorderLayout.NORTH, t);
home.show();
}
public void stop() {
current = Display.getInstance().getCurrent();
}
public void destroy() {
}
}
I would therefore appreciate any advice and guidance on exactly where I am going wrong and how to implement the suggested changes in my code.
I'm assuming the file under src does indeed end with the extension db as the Windows hidden extensions nonsense is turned on.
This code will NOT open a db placed in src:
db = Display.getInstance().openOrCreate("FoodAndBeverage.db");
You need to do something like this to implicitly initialize the DB the first time the app is installed:
String path = Display.getInstance().getDatabasePath("FoodAndBeverage.db");
FileSystemStorage fs = FileSystemStorage.getInstance();
if(!fs.exists(path)) {
try (InputStream is = Display.getInstance().getResourceAsStream(getClass(), "/FoodAndBeverage.db");
OutputStream os = fs.openOutputStream(path)) {
Util.copy(is, os);
} catch(IOException err) {
Log.e(err);
}
}
db = Display.getInstance().openOrCreate("FoodAndBeverage.db");
Notice that the code above doesn't check for updates of the DB so assuming the DB is read only you might want to update/merge it with app updates.
The above code doesn't work on Android device, this works only on simulator. I have tested multiple times in the android device. In the real android device ,the database is not loaded at all, shows sql exception error
"No such table sql exception".
Looks like preloaded sqlite .db file is never tested on real Android device.

ASP.NET Bundling and Minification removing license comments? [duplicate]

I have found this link:
http://giddyrobot.com/preserving-important-comments-in-mvc-4-bundles/
It shows how to do this same thing for JavaScript and I have used it to make an attempt for StyleBundles, but I'm unsure if it's doing things correctly on the backend.
Is the source code available? If not does anyone know if this seems right? All I want to keep is comments that start with /*! so that licenses for open source projects like normalize get included properly in production.
Here is what I have so far:
public static void RegisterBundles(BundleCollection bundles)
{
// Allows us to keep /*! comments for licensing purposes
var cssBundleSettings = new CssSettings
{
CommentMode = CssComment.Important
};
}
public class ConfigurableStyleBundle : Bundle
{
public ConfigurableStyleBundle(string virtualPath, CssSettings cssSettings) :
this(virtualPath, cssSettings, null) { }
public ConfigurableStyleBundle(string virtualPath, CssSettings cssSettings, string cdnPath) :
base(virtualPath, cdnPath, new[] { new ConfigurableCSSTransform(cssSettings) })
{
// commented out from js concatenation token not sure if this one should have one
//base.ConcatenationToken = ";";
}
}
[ExcludeFromCodeCoverage]
public class ConfigurableCSSTransform : IBundleTransform
{
private readonly CssSettings _cssSettings;
public ConfigurableCSSTransform(CssSettings cssSettings)
{
_cssSettings = cssSettings;
}
public void Process(BundleContext context, BundleResponse response)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (response == null)
{
throw new ArgumentNullException("response");
}
if (!context.EnableInstrumentation)
{
var minifier = new Minifier();
var content = minifier.MinifyStyleSheet(response.Content, _cssSettings);
if (minifier.ErrorList.Count > 0)
{
GenerateErrorResponse(response, minifier.ErrorList);
}
else
{
response.Content = content;
}
}
response.ContentType = "text/css";
}
internal static void GenerateErrorResponse(BundleResponse bundle, IEnumerable<object> errors)
{
var content = new StringBuilder();
content.Append("/* ");
content.Append("CSS MinifyError").Append("\r\n");
foreach (object current in errors)
{
content.Append(current.ToString()).Append("\r\n");
}
content.Append(" */\r\n");
content.Append(bundle.Content);
bundle.Content = content.ToString();
}
}
All of this is wrapped in public class BundleConfig and gets called from Global.asax.
I'm just wondering if CssComment.Important could have negative effects and remove too much and if this seems to be doing what I want it to? When I have tested it everything seems to look correct styling wise, but it doesn't hurt to get some eyes seeing as this is probably useful for a lot of other ASP.NET devs who use open source libraries.
I don't think you've done anything incorrectly. Though I would approach it using the IBundleBuilder interface, as this will also keep regular comments out of production from prying eyes who switch user agent, like specified in How to prevent User-Agent: Eureka/1 to return source code. I show some steps on how to test against this in this related blog post.
public class ConfigurableStyleBuilder : IBundleBuilder
{
public virtual string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<BundleFile> files)
{
var content = new StringBuilder();
foreach (var file in files)
{
FileInfo f = new FileInfo(HttpContext.Current.Server.MapPath(file.VirtualFile.VirtualPath));
CssSettings settings = new CssSettings();
settings.CommentMode = Microsoft.Ajax.Utilities.CssComment.Important;
var minifier = new Microsoft.Ajax.Utilities.Minifier();
string readFile = Read(f);
string res = minifier.MinifyStyleSheet(readFile, settings);
if (minifier.ErrorList.Count > 0)
{
res = PrependErrors(readFile, minifier.ErrorList);
content.Insert(0, res);
}
else
{
content.Append(res);
}
}
return content.ToString();
}
private string PrependErrors(string file, ICollection<ContextError> errors )
{
var content = new StringBuilder();
content.Append("/* ");
content.Append("CSS MinifyError").Append("\r\n");
foreach (object current in errors)
{
content.Append(current.ToString()).Append("\r\n");
}
content.Append("Minify Error */\r\n");
content.Append(file);
return content.ToString();
}
private string Read(FileInfo file)
{
using (var r = file.OpenText())
{
return r.ReadToEnd();
}
}
}
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var cssBundle = new ConfigurableStyleBundle("~/Content/css");
cssBundle.Include("~/Content/stylesheet1.css");
cssBundle.Include("~/Content/stylesheet2.css");
bundles.Add(cssBundle);
//etc
}
}
I made a NuGet package for this (including a version for scripts) - https://www.nuget.org/packages/LicensedBundler/

Downloading Multiple files from FTP Server using JSCH

I want to download all the files from FTP server using JSCH.
Below is the code snippet,
List<File> fileList = null;
Vector<ChannelSftp.LsEntry> list = sftpChannel.ls(remoteFolder);
for (ChannelSftp.LsEntry file : list) {
if( getLog().isDebugEnabled() ){
getLog().debug("Retrieved Files from the folder is"+file);
}
if (!(new File(file.getFilename())).isFile()) {
continue;
}
fileList.add(new File(remoteFolder,file.getFilename())) ;
return fileList;
The method will return List, for another method to download the files from the remote server using sftpChannel.get(src,dest) ;
Please let me know if the code is ok.
I don't have an environment to test, so can't confirm it.
But somewhat similar code i wrote for FTPClient and it works.
Appreciate your help.
You can use SftpATTRS to get the file information. You can declare a wrapper class to store file information. An example shown below.
private class SFTPFile
{
private SftpATTRS sftpAttributes;
public SFTPFile(LsEntry lsEntry)
{
this.sftpAttributes = lsEntry.getAttrs();
}
public boolean isFile()
{
return (!sftpAttributes.isDir() && !sftpAttributes.isLink());
}
}
Now you can use this class to test if the LsEntry is a file
private List<SFTPFile> getFiles(String path)
{
List<SFTPFile> files = null;
try
{
List<?> lsEntries = sftpChannel.ls(path);
if (lsEntries != null)
{
files = new ArrayList<SFTPFile>();
for (int i = 0; i < lsEntries.size(); i++)
{
Object next = lsEntries.get(i);
if (!(next instanceof LsEntry))
{
// throw exception
}
SFTPFile sftpFile = new SFTPFile((LsEntry) next);
if (sftpFile.isFile())
{
files.add(sftpFile);
}
}
}
}
catch (SftpException sftpException)
{
//
}
return files;
}
Now you can use sftpChannel.get(src,dest) ; to download files.

Resources