Hi I'm trying to save a file from a Java Webstart Application.
public class Main {
public static void main(String[] args) {
try {
FileSaveService fos = (FileSaveService) ServiceManager.lookup("javax.jnlp.FileSaveService");
//open Dialog
FileContents fc = fos.saveFileDialog("c:/data", null, new ByteArrayInputStream("Hallo Welt".getBytes()), "name.txt");
System.out.println("FileContents: " + fc);
} catch (UnavailableServiceException e) {
System.err.println("***" + e + "***");
} catch (IOException e) {
System.err.println("***" + e + "***");
}
//wait a minute
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
System.exit(0);
}
}
Everything works except that the dialog that comes up looks like a "open" file dialog, not like a "save" file dialog:
Any help would be appreciated.
The File-Open-dialog is necessary. You first need to let the user choose where to save the data. Thus a previous call to openFileDialog is absolute necessary for a jnlp-application. You are not allowed to directly save to a specific location like c:
If you follow the mentioned link (http://docs.oracle.com/javase/6/docs/technotes/guides/javaws/developersguide/examples.html#FileSaveService) you should be successful.
EDIT:
for clarification.
Saving via javax.jnlp.FileSaveService does exactly need one call. For instance calling saveFileDialog() like this should be sufficient:
fss.saveFileDialog(null, null, new ByteArrayInputStream("Hallo Welt".getBytes() ), "newFileName.txt");
The necessity of one User-Dialogue is due to the anonymizing nature of jnlp, where your application should not get any hint about the user-filesystem.
However, I have to admit, that this was not your question.
Your main trouble comes from the java app everytime presenting the "open-dialogue" instead of the "save-dialogue".
This should not happen! If I may humbly assume from your snippet where you call fos.saveFileDialog: did you just initialize fos by the FileOpenService instead of the FileSaveService?
More details on the FileSaveService can be found here: http://docs.oracle.com/javase/7/docs/jre/api/javaws/jnlp/javax/jnlp/FileSaveService.html
This seems to be fixed in JRE bersion 1.7.0_21-b11 Java HotSpot(TM) 64-Bit Server VM
And there it is: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=2227257
Related
I'm trying to iteratively (part of automation):
Create backup of the projects in solution (physical files on the filesystem)
Using Microsoft.Build programmatically load and change projects inside of the solution (refernces, includes, some other properties)
Build it with console call of msbuild
Restore projects (physically overriding patched versions from backups)
This approach works well for first iteration, but for second it appears that it does not load restored projects and trying to work with values that I patched on the first iteration. It looks like projects are cached: inside of the csproj files I see correct values, but on the code I see previously patched values.
My best guess is that Microsoft.Build is caching solution/projects in the context of the current process.
Here is code that is responsible to load project and call method to update project information:
private static void ForEachProject(string slnPath, Func<ProjectRootElement> patchProject)
{
SolutionFile slnFile = SolutionFile.Parse(slnPath);
var filtredProjects = slnFile
.ProjectsInOrder
.Where(prj => prj.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat);
foreach (ProjectInSolution projectInfo in filtredProjects)
{
try
{
ProjectRootElement project = ProjectRootElement.Open(projectInfo.AbsolutePath);
patchProject(project);
project.Save();
}
catch (InvalidProjectFileException ex)
{
Console.WriteLine("Failed to patch project '{0}' with error: {1}", projectInfo.AbsolutePath, ex);
}
}
}
There is Reload method for the ProjectRootElement that migh be called before iteraction with content of the project.
It will enforce Microsoft.Build to read latest information from the file.
Code that is working for me:
private static void ForEachProject(string slnPath, Func<ProjectRootElement> patchProject)
{
SolutionFile slnFile = SolutionFile.Parse(slnPath);
var filtredProjects = slnFile
.ProjectsInOrder
.Where(prj => prj.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat);
foreach (ProjectInSolution projectInfo in filtredProjects)
{
try
{
ProjectRootElement project = ProjectRootElement.Open(projectInfo.AbsolutePath);
project.Reload(false); // Ignore cached state, read actual from the file
patchProject(project);
project.Save();
}
catch (InvalidProjectFileException ex)
{
Console.WriteLine("Failed to patch project '{0}' with error: {1}", projectInfo.AbsolutePath, ex);
}
}
}
Note: It better to use custom properties inside of the project and provide it for each msbuild call instead of physical project patching. Please consider it as better solution and use it if possible.
In my project, I have the player info being saved out to a JSON file. I am encrypting the information before I save it but if the player goes into the file and happens to delete one character of the file, when the game loads, because it depends upon that file, the game freezes up. I do have it so that a new player info is created if no file is detected, but if the file is there and they mess with it, is there any way of detecting that and correcting it before the game tries to load it. I am using JSONUtility built into Unity.
Use a try/catch when loading the JSON file
try
{
JsonUtility.FromJSON(...)
}
catch (FileNotFoundException e)
{
Print("The file was not found: '{e}'");
}
catch (DirectoryNotFoundException e)
{
Print("The directory was not found: '{e}'");
}
catch (IOException e)
{
Print("The file could not be opened: '{e}'");
}
However, this shouldn't be that important. If a player is trying to mess with the game files and you are worried about the program crashing, you shouldn't because that player shouldn't have been editing game files.
I think using both hashing and try/catch checking would be the better solution
You can just try to open the file then compare its hash with the hash you saved in the last game session:
private void LoadSave()
{
try
{
JsonUtility.FromJson("filename", ...);
string oldHash = PlayerPrefs.GetString("importantSaveFileHash");
string newHash = CalculateMd5("filename");
if (oldHash == null || oldHash == newHash)
{
//recalculate hash every time you change the save file
//you can also encrypt this hash for better security
PlayerPrefs.SetString("importantSaveFileHash", newHash);
//RESULT: Save file is cool!
}
else
{
//RESULT: Save file was modified!
}
}
catch (Exception e)
{
//RESULT: broken file
}
}
private static string CalculateMd5(string filename)
{
using (var md5 = MD5.Create())
{
using (var stream = File.OpenRead(filename))
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
Btw you can just use PlayerPrefs to store all the game state (I know that sometimes its important to have visible and readable save file) - choose what you need)
UPD: its not good to store game state in PlayerPrefs - use any another way instead (read comments)
I'm using the following when trying to open a local file:
some document
When I click the above in a browser, it opens Finder to the folder. But does not open the file. Should I be doing something else to have the file open in Numbers?
You cannot open local files on the client. This would be a huge security risk.
You can link to files on your server (like you did) or you can ask the client for a file using <input type="file">
You can only open some types of files in browsers, like html css js and mp4, otherwise the browser will want to download it. Also remember that browsers replace spaces with %20. I recommend right clicking the file and opening it with chrome then copy that link and using it.
You can open files that are local as long as it is a file that is on the file that is trying to open another file is local.
Your issue is likely the space in the document name. Try this instead:
some document
The %20 will be read by your browser as a space.
Update
The other answer points out something I missed. The .numbers extension will not be able to be opened directly by your browser. Additionally the other answer describes the security risk this could create.
The File API in HTML 5 now allows you to work with local files directly from JS (after basic user interaction in selecting the file(s), for security).
From the Mozilla File API docs:
"The File interface provides information about files and allows JavaScript in a web page to access their content.
File objects are generally retrieved from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement."
For more info and code examples, see the sample demo linked from the same article.
This might not be what you're trying to do, but someone out there may find it helpful:
If you want to share a link (by email for example) to a network file you can do so like this:
file:///Volumes/SomeNetworkFolder/Path/To/file.html
This however also requires that the recipient connects to the network folder in finder --- in menu bar,
Go > Connect to Server
enter server address (e.g. file.yourdomain.com - "SomeNetworkFolder" will be inside this directory) and click Connect. Now the link above should work.
Here is the alternative way to download local file by client side and server side effort:
<a onclick='fileClick(this)' href="file://C:/path/to/file/file.html"/>
js:
function fileClick(a) {
var linkTag = a.href;
var substring = "file:///";
if (linkTag.includes(substring)) {
var url = '/v/downloadLocalfile?path=' +
encodeURIComponent(linkTag);
fileOpen(url);
}
else {
window.open(linkTag, '_blank');
}
}
function fileOpen(url) {
$.ajax({
url: url,
complete: function (jqxhr, txt_status) {
console.log("Complete: [ " + txt_status + " ] " + jqxhr);
if (txt_status == 'success') {
window.open(url, '_self');
}
else {
alert("File not found[404]!");
}
// }
}
});
}
Server side[java]:
#GetMapping("/v/downloadLocalfile")
public void downloadLocalfile(#RequestParam String path, HttpServletResponse
response) throws IOException, JRException {
try {
String nPath = path.replace("file:///", "").trim();
File file = new File(nPath);
String fileName = file.getName();
response.setHeader("Content-Disposition", "attachment;filename=" +
fileName);
if (file.exists()) {
FileInputStream in = new FileInputStream(file);
response.setStatus(200);
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int numBytesRead;
while ((numBytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, numBytesRead);
}
// out.flush();
in.close();
out.close();
}
else {
response.setStatus(404);
}
} catch (Exception ex) {
logger.error(ex.getLocalizedMessage());
}
return;
}
You can expose your entire file system in your browser by using an http server.
caddy2 server
caddy file-server --listen :2022 --browse --root /
serves the root file system at http://localhost:2022/
python3 built-in server
python3 -m http.server
serves current dir on http://localhost:8000/
python2 built-in server
python3 -m SimpleHTTPServer
serves current dir on http://localhost:8000/
This s
In the below code, the continuation of CreateFileAsync() neither prints nor accesses pdone. However, the zero-length file, Hello.txt, is created.
auto pdone = make_shared<bool>(false);
create_task(folderLocal->CreateFileAsync("Hello.txt", CreationCollisionOption::ReplaceExisting)).then([pdone](StorageFile ^file) {
OutputDebugString(L"In CreateFileAsync continuation!\n");
*pdone = true;
});
create_task([pdone]{
OutputDebugString(L"In my task!\n");
});
create_async([pdone]{
OutputDebugString(L"In my async!\n");
});
while (!*pdone) {}
OutputDebugString(L"Done!\n");
In the debugger:
In my task!
In my async!
I'm not very familiar with debugging WinRT threads yet, but I do not see any obvious exception or any reason the continuation to the async operation should not execute. The target platform is the Hololens emulator.
Any thoughts are appreciated.
Thanks!
Harry's comment above is most likely the culprit - if you initiated this on a UI thread then by default the C++ tasks library (PPL) will try to schedule the completion on the same thread. This will never happen if you are spinning the thread waiting for the completion to happen (classic deadlock).
If you must do this (although you really should try and avoid it) you need to use a "continuation context" to tell PPL to run the continuation somewhere else.
Here's an example. First, basic XAML (just paste inside the Grid of a blank C++ XAML project):
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Hang the UI thread" Click="Hang"/>
<Button Content="Do not do this" Click="DoNotDoThis"/>
</StackPanel>
And the code (just paste after the MainPage constructor):
using namespace Windows::Storage;
using namespace concurrency;
void DoIt(task_continuation_context& context)
{
auto folder = ApplicationData::Current->LocalFolder;
auto done = std::make_shared<bool>(false);
create_task(folder->CreateFileAsync(L"x", CreationCollisionOption::ReplaceExisting))
.then([done](StorageFile^ file) mutable
{
OutputDebugString(L"Done creating file\n");
*done = true;
}, context);
OutputDebugString(L"Going to wait... DO NOT DO THIS IN PRODUCTION CODE!\n");
while (!*done)
;
OutputDebugString(L"Done waiting\n");
}
void MainPage::Hang(Platform::Object^ sender, RoutedEventArgs^ e)
{
OutputDebugString(L"Starting Hang\n");
// The default context == the UI thread (if called from UI)
DoIt(task_continuation_context::use_default());
OutputDebugString(L"Ending Hang\n");
}
void MainPage::DoNotDoThis(Platform::Object^ sender, RoutedEventArgs^ e)
{
OutputDebugString(L"Starting DoNotDoThis\n");
// An arbitrary context will pick another thread (not the UI)
DoIt(task_continuation_context::use_arbitrary());
OutputDebugString(L"Ending DoNotDoThis\n");
}
As noted, you shouldn't do this. If you need synchronous File I/O, and you're accessing files in your own package, use the Win32 API CreateFile2. If you need to access files outside of your package (eg, from a file picker or the photos library) you should use a fully-async programming approach.
I believe using task_continuation_context::use_arbitarty() is the correct way of doing this, however I think microsoft suggests using it slightly differently unless i have misunderstood this link (scroll all the way to the bottom): https://msdn.microsoft.com/en-us/library/hh750082.aspx
create_task(folderLocal->CreateFileAsync("Hello.txt", CreationCollisionOption::ReplaceExisting)).then([pdone](StorageFile ^file) {
OutputDebugString(L"In CreateFileAsync continuation!\n");
*pdone = true;
}, task_continuation_context::use_arbitrary());
In my program I have a string which contains URLs separated by /n (One per line)
Let's say the string is called "links". I want to take this string and generate a HTML file that will automatically open in my default browser which will make each URL a hyperlink (one per line). How would I make such a report not using any third party components using WPF C# 4.0? I want the report to be generated by clicking a button called "Export".
There are plenty of ways to do this, but here is a quick and dirty example (debugging may be necessary since I wrote this on the fly). [Edit: Now uses Uri objects to formulate the actual address.]
private void export_Click(object sender, RoutedEventArgs e)
{
string tempFileName = "list.html";
string links = "http://www.google.com/#sclient=psy&hl=en&site=&source=hp&q=test+me&aq=f&aqi=&aql=&oq=&gs_rfai=&pbx=1&fp=ddfbf15c2e2f4021\nhttp://www.testme.com/Test-Prep.html?afdt=Q3RzePF0jU8KEwja-5WM7PqkAhUUiZ0KHaoG_wcYASAAMJbwoAM4MEC4w6uX7dS53gdQlvCgA1CEra8PUJzr_xNQg73wFVCKttweUJStzNoBUNv67ZsD";
List<Uri> uriCollection = new List<Uri>();
foreach (string url in links.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
uriCollection.Add(new Uri(url));
}
// Create temporary file.
using (TextWriter writer = new StreamWriter(tempFileName))
{
try
{
writer.WriteLine("<html>");
writer.WriteLine("<head><title>Links</title></head>");
writer.WriteLine("<body>");
writer.WriteLine("<p>");
foreach (Uri uri in uriCollection)
{
writer.WriteLine("{1}<br />", uri.OriginalString, uri.Host);
}
writer.WriteLine("</p>");
writer.WriteLine("</body>");
writer.WriteLine("</html>");
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError(ex.Message);
}
finally
{
writer.Close();
}
}
// Open browser with temporary file.
if (File.Exists(tempFileName))
{
System.Diagnostics.Process.Start(tempFileName);
}
}
The 'Export' button is wired to the event 'export_Click'. I hard-coded the the string with '\n''s for the example. Simply break these apart using split and write a temporary file creating the HTML you need. Then, once the file is completed, you can open it using the Process.Start() method.
Ideally this can be done using DataBinding and other elements available in WPF if the need to open a browser window was not required. This would also remove any external dependencies the program may have.