Glassfish - uploading images - doing it right - java

I am on latest glassfish (3.1.2) - so no need for apache FileItem and no bugs with getPart(). I read that the best practice on uploading images is saving them on the file system (see here for instance). I am editing already existing code - smelly at that - so I had the idea to do :
Part p1 = request.getPart("file");
System.out.println("!!!!!P1 : " + p1);
Prints :
!!!!!P1 : File name=DSC03660.JPG,
StoreLocation=C:\_\glassfish3\glassfish\domains\domain1\generated\jsp\elkethe\upload_7cb06306_138b413999a__7ffa_00000000.tmp,
size=2589152bytes, isFormField=false, FieldName=file
newlines mine. In the code people are doing :
if (request.getParameter("crop") != null) {
// get path on the server
String outputpath = this.getServletContext().getRealPath(
"images/temp/" + session.getId() + ".jpg");
// store photo
InputStream is = p1.getInputStream();
createPhoto(is, outputpath);
session.setAttribute("photo_path", "images/temp/" + session.getId()
+ ".jpg");
response.sendRedirect("cropping");
return;
}
Where
private void createPhoto(InputStream is, String outputpath) {
FileOutputStream os = null;
try {
os = new FileOutputStream(outputpath);
// write bytes taken from uploaded file to target file
int ch = is.read();
while (ch != -1) {
os.write(ch);
ch = is.read();
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
Helpers.close(os);
}
}
Now what happens is that the file is uploaded in the StoreLocation (???) on submitting the form so apparently all this p1.getInputStream() is for naught.
My questions are :
what is StoreLocation ? How tmp are those glassfish uploads ? Where are all those parameters set ? I did read BalusC' tutorial - but there is no mention of StoreLocation (google is not very helpful either).
What would be a more professional way of handling the situation - including keeping the photos outside the webroot - but using facilities glassfish provides (if it does provide) ?
Even p1 printing so nice escapes me (it does not seem to Override toString())
Interested in tips even in how should one rename the photos etc (is this sessionID thing Right ? - check also the time trick) :
if (request.getParameter("save") != null) {
long time = System.currentTimeMillis();
String path = "images/upload/" + session.getId() + time + ".jpg";
String outputpath = this.getServletContext().getRealPath(path);
// store photo
InputStream is = p1.getInputStream();
createPhoto(is, outputpath);
// etc
}

Good practice is to pick a path on the filesystem where photos will be uploaded. Often this path is programmed to be configurable via java system property (eg: by passing -Dcom.mycompany.uploadPath=/path/to/photos/dir system property on JVM arguments).
You can also use java system propeties to find environment specific path: user.dir, user.home etc. See System Properties on Java SE Tutorial. Or to use glassfish-relative path, see glassfish system properties.
Once you have reference to Part, it's just about doing file IO to copy the uploaded file into this upload path, eg:
Part part = // obtain part somehow..
String photoFileName = // build a file name somehow..
InputStream photoInputStream = part.getInputStream();
FileOutputStream photoOutputStream = new FileOutputStream(System.getProperty("com.mycompany.uploadPath") + File.separator + photoFileName);
IOUtils.copy(photoInputStream, photoOutputStream);
// close streams here...
Code above uses apache IOUtils for convenience but feel free to write your own copy method. You should also add exception handling method

What is StoreLocation ? How tmp are those glassfish uploads ? Where are all those parameters set ?
StoreLocation is just the the java.io.File object for the FileItem's
data's temporary location on the disk. Resides in javax.servlet.context.tempdir which defaults to %GLASSFISH_HOME%\domains\domain1\generated\jsp\webApp. Those uploads are as tmp as anything (The lifetime of the file is tied to the lifetime of the FileItem instance; the file will be deleted when the instance is garbage collected - from here). Haven't yet managed to change the value of javax.servlet.context.tempdir programmatically (comment please) - it is the tempdir property of the sun-web-app element of the sun-web.xml.
What would be a more professional way of handling the situation - including keeping the photos outside the webroot - but using facilities glassfish provides (if it does provide) ?
Well a more professional way is to Use Part.write() to move the file to the desired location. Due to glassfish implementation though you can't supply an absolute path to write - a chore. I asked here.
As to where to save the file : https://stackoverflow.com/a/18664715/281545
That is for saving the file - to serve it from a location outside the app you need to define "alternatedocroot" properties in the sun-web.xml (or glassfish-web.xml).
Even p1 printing so nice escapes me (it does not seem to Override toString())
Oh yes it does
Interested in tips even in how should one rename the photos etc (is this sessionID thing Right ? - check also the time trick)
No it is not - I tend towards File#createTempFile() - anyway this is a different question asked here

Related

How can fix this error while trying to upload a file to a online database using JSF 2.2? [duplicate]

I would like to be able to upload files in my JSF2.2 web application, so I started using the new <h:inputFile> component.
My only question is, how can I specify the location, where the files will be saved in the server? I would like to get hold of them as java.io.File instances. This has to be implemented in the backing bean, but I don't clearly understand how.
JSF won't save the file in any predefined location. It will basically just offer you the uploaded file in flavor of a javax.servlet.http.Part instance which is behind the scenes temporarily stored in server's memory and/or temporary disk storage location which you shouldn't worry about.
Important is that you need to read the Part as soon as possible when the bean action (listener) method is invoked. The temporary storage may be cleared out when the HTTP response associated with the HTTP request is completed. In other words, the uploaded file won't necessarily be available in a subsequent request.
So, given a
<h:form enctype="multipart/form-data">
<h:inputFile value="#{bean.uploadedFile}">
<f:ajax listener="#{bean.upload}" />
</h:inputFile>
</h:form>
You have basically 2 options to save it:
1. Read all raw file contents into a byte[]
You can use InputStream#readAllBytes() for this.
private Part uploadedFile; // +getter+setter
private String fileName;
private byte[] fileContents;
public void upload() {
fileName = Paths.get(uploadedFile.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
try (InputStream input = uploadedFile.getInputStream()) {
fileContents = input.readAllBytes();
}
catch (IOException e) {
// Show faces message?
}
}
Note the Path#getFileName(). This is a MSIE fix as to obtaining the submitted file name. This browser incorrectly sends the full file path along the name instead of only the file name.
In case you're not on Java 9 yet and therefore can't use InputStream#readAllBytes(), then head to Convert InputStream to byte array in Java for all other ways to convert InputStream to byte[].
Keep in mind that each byte of an uploaded file costs one byte of server memory. Be careful that your server don't exhaust of memory when users do this too often or can easily abuse your system in this way. If you want to avoid this, better use (temporary) files on local disk file system instead.
2. Or, write it to local disk file system
In order to save it to the desired location, you need to get the content by Part#getInputStream() and then copy it to the Path representing the location.
private Part uploadedFile; // +getter+setter
private File savedFile;
public void upload() {
String fileName = Paths.get(uploadedFile.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
savedFile = new File(uploads, fileName);
try (InputStream input = file.getInputStream()) {
Files.copy(input, savedFile.toPath());
}
catch (IOException e) {
// Show faces message?
}
}
Note the Path#getFileName(). This is a MSIE fix as to obtaining the submitted file name. This browser incorrectly sends the full file path along the name instead of only the file name.
The uploads folder and the filename is fully under your control. E.g. "/path/to/uploads" and Part#getSubmittedFileName() respectively. Keep in mind that any existing file would be overwritten, you might want to use File#createTempFile() to autogenerate a filename. You can find an elaborate example in this answer.
Do not use Part#write() as some prople may suggest. It will basically rename the file in the temporary storage location as identified by #MultipartConfig(location). Also do not use ExternalContext#getRealPath() in order to save the uploaded file in deploy folder. The file will get lost when the WAR is redeployed for the simple reason that the file is not contained in the original WAR. Always save it on an absolute path outside the deploy folder.
For a live demo of upload-and-preview feature, check the demo section of the <o:inputFile> page on OmniFaces showcase.
See also:
Write file into disk using JSF 2.2 inputFile
How to save uploaded file in JSF
Recommended way to save uploaded files in a servlet application

Java: store configuration variables in a file

I'm developing a program with NetBeans 8.0 and JavaFX Scene Builder 2.0 that need store some variables in a file, where admin users can modify it when needed, (like change server IP address, or a number value from a no editable textfield) and if they close and load again the program, the changes made in variables are kept. Like any settings section of a program.
I just try do it with the Properties file, but i have problems to store it in the same folder as .jar file. When the program execute the line new FileOutputStream("configuration.properties"); the file is created at root of the disk. As the folder of the file can be stored anywhere, i not know how indicate the right path.
Creating the properties file in the package of the main project and using getClass().getResourceAsStream("configuration.properties"); i can read it but then i can not write in for change values of variables.
Is there a better method to create a configuration file? Or properties file is the best option for this case?
My other question is whether it is possible to prevent access to the contents of the file or encrypt the content?
PD: I've been testing this part of the code in Linux operating system currently, but the program will be used in Windows 7 when ready.
If you use Maven, you can store your property files in your resources folder, say resources/properties/. When you need to load them, do this:
private Properties createProps(String name)
{
Properties prop = new Properties();
InputStream in = null;
try
{
in = getClass().getResourceAsStream(name);
prop.load(in);
}
catch (IOException ex)
{
System.err.println("failed to load \"" + name + "\": " + ex);
}
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
System.err.println("failed to close InputStream for \"" + name + "\":\n" + FXUtils.extractStackTrace(ex));
}
}
return prop;
}
Where name is the full path to your properties file within your resources folder. For example, if you store props.properties in resources/properties/, then you would pass in properties/props.properties.
I am not 100% sure if you can carry over this exact procedure to a non-Maven project. You'd need to instruct whatever compiler tool you are using to also include your property files.
As far as your final question goes, in regards to encrypting your properties, I would consider posting that as a separate question (after having done thorough research to try to discover an existing solution that works for you).
At last i found how obtain the absolute path from folder where is .jar file to create properties file in, and read/write it. Here is the code:
File file = new File(System.getProperty("java.class.path"));
File filePath = file.getAbsoluteFile().getParentFile();
String strPath = filePath.toString();
File testFile = new File(strPath+"/configuration.properties");
Tested in Ubuntu 13.04 And Windows 7 and it works.
For encrypt the properties values i found this thread that answer how do it.

Understanding Simple XML Parser - New File Output - Java

I am trying to learn how to use the Simple XML Framework as detailed in this thread : Best practices for parsing XML.
I am using the following code :
public class SimpleXMLParserActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
Serializer serializer = new Persister();
Example example = new Example("Example message", 123);
File result = new File("example.xml");
try {
Log.d("Start", "Starting Serializer");
serializer.write(example, result);
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("Self", "Error");
e.printStackTrace();
}
}
}
I am having a problem understanding the line
File result = new File("example.xml");
1) Does this line create a new file in my app called example.xml ? If so where is this file located.
2) Or does this line look for an existing file called example.xml and then add to it ? If so where should the example.xml file be placed in my app bundle so that it can be found. I do notice at the moment I am getting an error message :
java.io.FileNotFoundException: /example.xml (Read-only file system)
Thank you.
File result = new File("example.xml")
This line will just store the filename "example.xml" in a new File object. There is no check if that file actually exists and it does not try to create it either.
A file without specifying an absolute path (starting with / like new File("/sdcard/example.xml")) is considered to be in the current working directory which I guess is / for Android apps (-> /example.xml (Read-only file system))
I guess serializer.write(example, result); tries to create the actual file for your but fails since you can't write to '/'.
You have to specify a path for that file. There are several places you can store files, e.g.
Context#getFilesDir() will give you a place in your app's home directory (/data/data/your.package/files/) where only you can read / write - without extra permission.
Environment#getExternalStorageDirectory() will give you the general primary storage thing (might be /sdcard/ - but that's very different for devices). To write here you'll need the WRITE_EXTERNAL_STORAGE permission.
there are more places available in Environment that are more specialized. E.g. for media files, downloads, caching, etc.
there is also Context#getExternalFilesDir() for app private (big) files you want to store on the external storage (something like /sdcard/Android/data/your.package/)
to fix your code you could do
File result = new File(Environment.getExternalStorageDirectory(), "example.xml");
Edit: either use the provided mechanisms to get an existing directory (preferred but you are limited to the folders you are supposed to use):
// via File - /data/data/your.package/app_assets/example.xml
File outputFile = new File(getDir("assets", Context.MODE_PRIVATE), "example.xml");
serializer.write(outputFile, result);
// via FileOutputStream - /data/data/your.package/files/example.xml
FileOutputStream outputStream = openFileOutput("example.xml", Context.MODE_PRIVATE);
serializer.write(outputStream, result);
or you may need to create the directories yourself (hackish way to get your app dir but it should work):
File outputFile = new File(new File(getFilesDir().getParentFile(), "assets"), "example.xml");
outputFile.mkdirs();
serializer.write(outputFile, result);
Try to avoid specifying full paths like "/data/data/com.simpletest.test/assets/example.xml" since they might be different on other devices / Android versions. Even the / is not guaranteed to be /. It's safer to use File.separatorChar instead if you have to.
2 solutions to do it cleanly :
use openFileOutput to write a private file in the application private directory (which could be located in the internal memory or the external storage if the app was moved there). See here for a snippet
or use the File constructor to create the File anywhere your app has write access. This is if you want to store the file on the SDCard for example. Instantiating a file doesn't create it on the file system, unless you start writiung to it (with FileOutputStream for example)
I'd recommend approach 1, it's better for users because these files get erased when your app is uninstalled. If the file is large, then using the External Storage is probably better.
What I read on the Android pages, I see it creates a file with that name:
File constructor
I think it writes it to the /data/data/packagname directory
edit: the 'packagename' was not shown in the tekst above. I put it between brackets. :s
Try saving to /sdcard/example.xml.

Getting list of files inside a Java applet's JAR

I'm trying to fetch list of files using a method that apparently works well with non-applet Java code.
I'm fully aware it's messy; I'm just trying to get this to work for a school assignment. (I'm no fan of Java.)
CodeSource src = MemoryButtonHelper.class.getProtectionDomain().getCodeSource();
if (src == null) {
throw new Exception();
}
URL jar = src.getLocation();
System.out.println("Loading from " + jar);
JarFile zf=new JarFile(jar.toString()); //jar.openStream());
final Enumeration<JarEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
final JarEntry ze = entries.nextElement();
if(ze.getName().endsWith(".jpg") || ze.getName().endsWith(".png"))
{
System.out.println("Adding " + ze.getName());
slikeList.add(ze.getName());
}
}
zf.close();
Unfortunately, I'm getting a security exception.
java.security.AccessControlException: access denied (java.lang.RuntimePermission getProtectionDomain)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:374)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.Class.getProtectionDomain(Class.java:2106)
at hr.tvz.programiranje.java.deseti.helpers.MemoryButtonHelper.getSlike(MemoryButtonHelper.java:75)
… ad nauseam …
According to Java Console, exception appears to occur before the println("Loading from " + jar).
This is a bonus point assignment which specifically says that we must fetch the list of images from the JAR file. Since this is my first encounter with the applets, I'm not sure what I can do to fetch the list of images.
..we must fetch the list of images from the JAR file.
Who put them in there in the first place? If the answer is 'we did', the solution is simple.
Include a list of image path/names at a known location (e.g. images/imagelist.txt) in the Jar.
Obtain a reference to the list using getResource(String).
Read the list (probably using a line reader).
Apparently they want me to list the contents of the jar as-is, without extra metadata.
OK. If you can form an URL to (and thereby an InputStream from) the Zip file, it is possible to establish a ZipInputStream from it. This should work whether the URL is to a Zip/Jar cached on the local file-system or still at the server.
Get an URL to the Jar.
Establish a ZipInputStream from the URL.
Iterate the entries using getNextEntry() until null
Examine each one for a potential match and if it does, add it to an ArrayList.
Of course, you'll still need signed & trusted code to call for the protection domain.
Images are definitely in the same JAR
To get an URL to the Jar, try this (untested). Let's assume the applet is com.our.BookingApplet.
Obtain an URL to the Jar in which the class resides, using
URL urlToApplet = this.getClass().getResource("/com/our/BookingApplet.class")
String[] parts = urlToApplet.toString().split("!") will provide two parts, the first will be a String representation of the Jar URL.
Use that String to establish an URL, then use the URL as described in the previous update.
Thanks go to Andrew Thompson for his excellent answer! Definitely upvote his answer instead (or in addition) to this one, since without his help, I wouldn't be able to figure this out.
Here is the portion of the code which I came up with to use to fetch list of .jpg and .png files inside the JAR file. Note that you probably need to change the name of the package where MemoryGame class is stored, as well as change the name of the class itself.
List<String> slikeList = new ArrayList<String>();
URL urlToApplet = MemoryGame.class.getResource("/com/whoever/whatever/gui/MemoryGame.class");
String[] parts = urlToApplet.toString().split("!");
String jarURLString = parts[0].replace("jar:", "");
System.out.println("Loading from " + jarURLString);
URL jar = new URL(jarURLString);
URLConnection jarConnection = jar.openConnection();
JarInputStream jis = new JarInputStream(jarConnection.getInputStream());
JarEntry je = jis.getNextJarEntry();
while(je != null)
{
System.out.println("Inspecting " + je);
if(je.getName().endsWith(".jpg") || je.getName().endsWith(".png"))
{
System.out.println("Adding " + je.getName());
slikeList.add(je.getName());
}
je = jis.getNextJarEntry();
}
In case you wonder, slike means images in Croatian since a lot of variables named in the exercise specification are in Croatian.
Only "Signed" applets are allowed to access file system. If the jar file which you are reading from is located on your local file system, you will need to sign the applet first.
See this link for more information on how to sign applet.

How to atomically rename a file in Java, even if the dest file already exists?

I have a cluster of machines, each running a Java app.
These Java apps need to access a unique resource.txt file concurrently.
I need to atomically rename a temp.txt file to resource.txt in Java, even if resource.txt already exist.
Deleting resource.txt and renaming temp.txt doesn't work, as it's not atomic (it creates a small timeframe where resource.txt doesn't exist).
And it should be cross-platform...
For Java 1.7+, use java.nio.file.Files.move(Path source, Path target, CopyOption... options) with CopyOptions "REPLACE_EXISTING" and "ATOMIC_MOVE".
See API documentation for more information.
For example:
Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);
On Linux (and I believe Solaris and other UNIX operating systems), Java's File.renameTo() method will overwrite the destination file if it exists, but this is not the case under Windows.
To be cross platform, I think you'd have to use file locking on resource.txt and then overwrite the data.
The behavior of the file lock is
platform-dependent. On some platforms,
the file lock is advisory, which means
that unless an application checks for
a file lock, it will not be prevented
from accessing the file. On other
platforms, the file lock is mandatory,
which means that a file lock prevents
any application from accessing the
file.
try {
// Get a file channel for the file
File file = new File("filename");
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
// Use the file channel to create a lock on the file.
// This method blocks until it can retrieve the lock.
FileLock lock = channel.lock();
// Try acquiring the lock without blocking. This method returns
// null or throws an exception if the file is already locked.
try {
lock = channel.tryLock();
} catch (OverlappingFileLockException e) {
// File is already locked in this thread or virtual machine
}
// Release the lock
lock.release();
// Close the file
channel.close();
} catch (Exception e) {
}
Linux, by default, uses voluntary locking, while Windows enforces it. Maybe you could detect the OS, and use renameTo() under UNIX with some locking code for Windows?
There's also a way to turn on mandatory locking under Linux for specific files, but it's kind of obscure. You have to set the mode bits just right.
Linux, following System V (see System
V Interface Definition (SVID) Version
3), lets the sgid bit for files
without group execute permission mark
the file for mandatory locking
Here is a discussion that relates: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4017593
As stated here, it looks like the Windows OS doesn't even support atomic file rename for older versions. It's very likely you have to use some manual locking mechanisms or some kind of transactions. For that, you might want to take a look into the apache commons transaction package.
If this should be cross-platform I suggest 2 options:
Implement an intermediate service that is responsible for all the file accesses. Here you can use several mechanisms for synchronizing the requests. Each client java app accesses the file only through this service.
Create a control file each time you need to perform synchronized operations. Each java app that accesses the file is responsible checking for the control file and waiting while this control file exists. (almost like a semaphore). The process doing the delete/rename operation is responsible for creating/deleting the control file.
If the purpose of the rename is to replace resource.txt on the fly and you have control over all the programs involved, and the frequency of replacement is not high, you could do the following.
To open/read the file:
Open "resource.txt", if that fails
Open "resource.old.txt", if that fails
Open "resource.txt" again, if that fails
You have an error condition.
To replace the file:
Rename "resource.txt" to "resource.old.txt", then
Rename "resource.new.txt" to "resource.txt", then
Delete "resource.old.txt".
Which will ensure all your readers always find a valid file.
But, easier, would be to simply try your opening in a loop, like:
InputStream inp=null;
StopWatch tmr=new StopWatch(); // made up class, not std Java
IOException err=null;
while(inp==null && tmr.elapsed()<5000) { // or some approp. length of time
try { inp=new FileInputStream("resource.txt"); }
catch(IOException thr) { err=thr; sleep(100); } // or some approp. length of time
}
if(inp==null) {
// handle error here - file did not turn up after required elapsed time
throw new IOException("Could not obtain data from resource.txt file");
}
... carry on
You might get some traction by establishing a filechannel lock on the file before renaming it (and deleting the file you're going to overwrite once you have the lock).
-r
I solve with a simple rename function.
Calling :
File newPath = new File("...");
newPath = checkName(newPath);
Files.copy(file.toPath(), newPath.toPath(), StandardCopyOption.REPLACE_EXISTING);
The checkName function checks if exits.
If exits then concat a number between two bracket (1) to the end of the filename.
Functions:
private static File checkName(File newPath) {
if (Files.exists(newPath.toPath())) {
String extractRegExSubStr = extractRegExSubStr(newPath.getName(), "\\([0-9]+\\)");
if (extractRegExSubStr != null) {
extractRegExSubStr = extractRegExSubStr.replaceAll("\\(|\\)", "");
int parseInt = Integer.parseInt(extractRegExSubStr);
int parseIntPLus = parseInt + 1;
newPath = new File(newPath.getAbsolutePath().replace("(" + parseInt + ")", "(" + parseIntPLus + ")"));
return checkName(newPath);
} else {
newPath = new File(newPath.getAbsolutePath().replace(".pdf", " (" + 1 + ").pdf"));
return checkName(newPath);
}
}
return newPath;
}
private static String extractRegExSubStr(String row, String patternStr) {
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(row);
if (matcher.find()) {
return matcher.group(0);
}
return null;
}
EDIT: Its only works for pdf. If you want other please replace the .pdf or create an extension paramter for it.
NOTE: If the file contains additional numbers between brackets '(' then it may mess up your file names.

Categories

Resources