I have the following situation, within a servlet a create a file and then have to delete it.
When executing the file, I figured out that the file is still in the server, so I tried to remove it manually, I can't, I get the following message :
this file is opened by another program : javaw.exe
Here is my code :
public class GenerateFile extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("ok");
String fileName = request.getParameter("fileName");
Integer nbrParam = Integer.parseInt(request.getParameter("nbrParam"));
String[] valueParam = new String[nbrParam+1];
for(int i =1;i<=nbrParam;i++)
{ System.out.println(request.getParameter("param"+i));
valueParam[i]=request.getParameter("param"+i);
}
FileInputStream in = new FileInputStream("C:\\Users\\free\\Desktop\\myworkspace\\gestionRH\\WebRoot\\fiches\\"+fileName+".doc");
POIFSFileSystem fs = new POIFSFileSystem(in);
HWPFDocument doc = new HWPFDocument(fs);
Range r = doc.getRange();
for(int i=1;i<=nbrParam;i++)
{ System.out.println("<param"+i+">");
System.out.println(valueParam[i]);
r.replaceText("<param"+i+">", valueParam[i]);
}
File file = new File("C:\\Users\\free\\Desktop\\myworkspace\\gestionRH\\WebRoot\\fiches\\temp");
File temp = File.createTempFile("monfile",".doc",file);
String tempName =temp.getName();
doc.write( new FileOutputStream(temp));
OutputStream out = response.getOutputStream();
response.setContentType("application/rtf");
response.setHeader("Content-Disposition","attachment; filename=Decision");
FileInputStream in1 = new FileInputStream(temp);
byte[] buffer = new byte[4096];
int length;
while ((length = in1.read(buffer)) > 0){
out.write(buffer, 0, length);
}
in1.close();
out.flush();
System.out.println("C:\\Users\\free\\Desktop\\myworkspace\\gestionRH\\WebRoot\\fiches\\temp\\"+tempName);
File f = new File("C:\\Users\\free\\Desktop\\myworkspace\\gestionRH\\WebRoot\\fiches\\temp\\"+tempName);
f.delete();
return null;
}
}
You should close all the file-reading object instances. Besides, if you can delete the file manually, you should close java and then delete it, javaw is the process that launches java outside the console.
The problem is you are creating a new FileOutputStream(tempName) to write on that file, but never closing that outputstream (or another outputstream linked to it).
Do this:
FileOutputStream fos = newFileOutputStream(tempName);
// use it
fos.close(); // CLOSE IT!!
// then you can delete the file
Simplify
Maybe you could do the work another way, without temp files...
by example: doc.write(new FileOutputStream(tempName)) could be replaced by:
doc.write(response.getOutputStream());
This way doc sends its bytes directly to where you need them, not to a temp file eliminating the need for it.
The idea behind input/output streams is composing them. Input/OutputStream are the abstract base classes. And there are a lot of implementations:
based on memory: ByteArrayInput/OutputStream
based on files: FileInputOutputStream
compressing/decompressing to another outputstream: GZipInputOutputStream
and so on
The beauty of it is applying decorator pattern to add functionality. By example:
new GZipOutputStream(new ByteArrayOutputStream());
// creates an outputstreams that compress data received and send it to the other stream
// the BAOS then writes the received bytes to memory
new GZipOutputStream(new FileOutputStream());
// it's the same but sending compressed bytes to a file.
Seems like, you are not closing the file(out), thus it remains with the thread of this action, which is restricting it to get deleted.
Hope it helps.
maybe you should try ProcMon to find out what process exactly holds the file opened
For IO features, I would to suggest to use some kind of jar already provided by community.
For example, common-io.x-x.jar, spring-core.jar
Eg, org.apache.commons.io.FileUtils;
FileUtils.copyDirectory(from, to);
FileUtils.deleteDirectory(childDir);
FileUtils.forceDelete(springConfigDir);
FileUtils.writeByteArrayToFile(file, data);
org.springframework.util.FileSystemUtils;
FileSystemUtils.copyRecursively(from, to);
FileSystemUtils.deleteRecursively(dir);
good luck!
Whenever you open a file handler, you should close it. In a Java application that you want to run for a long period of time, you are strongly recommended to close all unused file handlers soon after you finish working with them.
Examples of common file handlers are FileOutputStream and FileInputstream. Here is a good example of how you open and close the FileOutputStream
FileOutputStream fos = null;
try {
fos = new FileOutputStream(tempName);
// do something
} catch (IOException ex) {
// deal with exceptions
} finally {
// close if fos is not null
if (fos != null) {
fos.close();
}
}
You should never do this:
doc.write( new FileOutputStream(temp));
because you can never close the file handler if it has no refernce to it.
Related
These are the related questions that might cause my question to be closed, even though I specify another question:
Java: How to write binary files? -> Doesn't really cover the point that I am talking about
create a binary file -> Absolutely doesn't cover the point
Editing a binary file in java -> They are talking about offsets and stuff, when I just need to write the data and stop
Binary files in java -> Vague.
And now to the point. I've got a file with a specific extension, to be more exact it's .nbs. I want to create a file and then write the specific data to it.
That might have sounded vague so let me show you the code I have started with.
try {
File bpdn = new File(getDataFolder() + "song.nbs");
if (!bpdn.exists()) {
bpdn.createNewFile();
}
FileOutputStream fos = new FileOutputStream(bpdn);
} catch (IOException e) {
e.printStackTrace();
}
I'll provide you even more details. So I've got a song.nbs file that I have created in the past, for myself. And now, whenever a person runs my application, I want it so there's a new song.nbs file with the exact contents of a file that I have on my PC right now. Therefore, I need to somehow get the bytes of my existing song.nbs and then copy and paste them in my Java application... or is it the way? I neither know how to get the bytes of my own file right now, nor do I know how to write them.
You need to create a resources folder. More info here.
Assuming your project structure is
ProjectName/src/main/java/Main.java
you can create a resources folder inside main/:
ProjectName/src/main/java/Main.java
ProjectName/src/main/resources/
Move your song.nbs you want to read inside resources/:
ProjectName/src/main/java/Main.java
ProjectName/src/main/resources/song.nbs
Now, get the InputStream of song.nbs stored there:
final ClassLoader classloader = Thread.currentThread().getContextClassLoader();
final InputStream is = classloader.getResourceAsStream("song.nbs");
Then, write this input stream to your new file:
final File bpdn = new File(getDataFolder() + "song.nbs");
if (!bpdn.exists()) bpdn.createNewFile();
final FileOutputStream os = new FileOutputStream(bpdn);
byte[] buffer = new byte[1024];
int read;
while ((read = is.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
I think I came up with a solution, but I am not sure if this is works. I'd appreciate if you would take a look.
try {
File bpdn = new File(getDataFolder() + "badpiggiesdrip.nbs");
if (!bpdn.exists()) {
bpdn.createNewFile();
}
FileOutputStream fos = new FileOutputStream(bpdn);
fos.write(new byte[] {
Byte.parseByte(Arrays.toString(Base64.getDecoder().decode(Common.myString)))
});
} catch (IOException e) {
e.printStackTrace();
}
Common.myString is just a string, that contains data of this type:
(byte) 0x21, (byte) 0x5a, .....
and it's encoded in Base64.
I get some very odd errors when using org.apache.commons.compress to read embedded archive files and I suspect it's my inexperience that is haunting me.
When running my code I get a variety of truncated zip file errors (along with other truncated file errors). I suspect it's my use of ArchiveInputStream
private final void handleArchive(String fileName, ArchiveInputStream ais) {
ArchiveEntry archiveEntry = null;
try {
while((archiveEntry = ais.getNextEntry()) != null) {
byte[] buffer = new byte[1024];
while(ais.read(buffer) != -1) {
handleFile(fileName + "/" + archiveEntry.getName(), archiveEntry.getSize(), new ByteArrayInputStream(buffer));
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
When I do this archiveEntry = ais.getNextEntry() does this effectively close my ais, and is there any way to read the bytes of embedded archive files using commons compress?
You re doing some wierd stuff it seems? For each archieve entry while your reading your archieve you re recursively calling your read archieve method which results in opening the next archieve while your parent code is still handling your previous archieve.
You should loop entirely through your archieve entry before handling any new archieve entry in your compressed file. Something like
ArArchiveEntry entry = (ArArchiveEntry) arInput.getNextEntry();
byte[] content = new byte[entry.getSize()];
LOOP UNTIL entry.getSize() HAS BEEN READ {
arInput.read(content, offset, content.length - offset);
}
as stated in the examples on the apache site
I need to save a file and download file in directory outside server context.
I am using Apache Tomacat
I am able to do this in directory present in webapps directory of application
If my directory structure is as follows,
--src
--WebContent
-- uploaddir
-- myfile.txt
Then I am able to download in by simply.
download
But, problem is when file is in some other directory say d:\\uploadedfile\\myfile.txt
then I wont be able to download it, as resource is not in server context as above.
I have file path to uuid mapping,
like,
d:\\uploadedfiles\\myfile.txt <-> some_uuid
then I want file should be downloaded, on click of following,
download
So, How to make file downloadable when it is outside the server context,
I heard about getResourceAsStream() method which would do this , But would any one help me on how to do this, probably with simple code snippet?
Try the below code which you can write in filedownloadservet. Fetch the file name from the request parameter and then read and write the file.
If you need to do some security checks then do that before processing the request.
File file = new File("/home/files", "file name which user wants to download");
response.setContentType(getServletContext().getMimeType(file.getName()));
response.setContentLength(file.length());
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(new FileInputStream(file));
outputStream = new BufferedOutputStream(response.getOutputStream());
byte[] buf = new byte[2048];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
//log it
}
}
// do the same for input stream also
}
here i found the answer,
response.setContentType("application/msword");
response.setHeader("Content-Disposition","attachment;filename=downloadname.doc");
File file=new File("d:\\test.doc");
InputStream is=new FileInputStream(file);
int read=0;
byte[] bytes = new byte[BYTES_DOWNLOAD];
OutputStream os = response.getOutputStream();
while((read = is.read(bytes))!= -1){
os.write(bytes, 0, read);
}
os.flush();
os.close();
Base path will not work that is for HTML and it works if the base path is also exposed by your web server which does not look like case here.
To download an arbitary file you need to open the file using a FileInputStream (and surround it by a buffered input stream), read a byte, then send that byte from your servlet to the client.
Then there are security concerns, so should google that (basically not give access to any file but only file that is to be shared, audit download etc as needed.
Again in your servlet set the mime type etc and then open a input stream and write the bytes to the output stream to client
I am currently facing some problem with a FileOutputStream in my Java code.
Actually I am using a FileOutputStream for creating a file, but then once the file is created there is no way for deleting it. As far as I could understand, this may come from the fact that the FileOutputstream is not closed.
On below my summarized code :
outFile = new FileOutputStream(dir+"\\"+fileName);
outFile.write("Test");
outFile.flush();
outFile.close();
outFile = null;
System.gc();
Then there is no way to delete the file, even "by hand". When my program is launched, I can't delete it on windows by a simple del. I also tried to remove content of the folder dir and it didn't worked neither, using this code :
static public void delDir( String place )
{
File path = new File( place );
System.out.println(path.exists());//return true
if( path.exists() )
{
File[] f = path.listFiles();
for( int j = 0 ; j < f.length ; j++ )
{
if( f[ j ].isDirectory() )
{
deleteDirectory( path+"\\"+f[ j ] );
}
f[ j ].delete();
}
}
}
So my question is :
How to close this file for a next delete (or how to delete it properly if we can't close it)?
It is a bug in Java. Yes it it rarely but they exists ;) Could you add after outFile.close()
outFile = null;
System.gc();
And then try to delete it. There are more possiblity if this is not working. Let me know.
UPDATE
For me it works:
public class FileDeleteExample {
public static void main(String[] args) throws Exception {
File f = new File("test.txt");
FileOutputStream outFile = null;
try {
outFile = new FileOutputStream(f);
outFile.write("Test".getBytes());
} finally {
outFile.flush();
outFile.close();
outFile = null;
System.gc();
}
f.delete();
}
}
UPDATE
I tried it with the example Sumit Singh mentioned by deleting the lines outFile=null; System.gc; and this works as well for me. So there should'nt be a problem with the FileOutputStream. Could you try the little example above and say whether it works or not?
UPDATE
void closeQuietly(FileOutputStream out) {
try { out.flush(); out.close(); } catch(Exception e) {}
}
Now just call the method in the finally block!
I had the same problem, the delete() method returned false for my File.
In my case, somewhere in between creating the file, writing to its FileOutputStream and deleting the file, i was using a FileInputStream and forgot to call close() for it.
So, maybe somewhere in your code you attached another stream to this file, and left it open.
Before finding the real source of the problem, i used a simle hack to temporarily fix this:
FileOutputStream fos = new FileOutputStream(myFile);
fos.close();
myFile.delete();
Right before calling delete on my File, i created another FileOutputStream over it and then just called close().
This unlocks all previuous locks on this file and lets you call delete().
Still it is not a good practice to do this. You should find out who uses your file and solve it the right way.
Well, the way to close a file output and input streams is:
name.close()
and your deletion code looks fine. My recommendation would be to use FileIO instead of FileOutputStream, unless you're using FileOutputStream for a good reason. Can you delete the file once the program closes?
Better to use FileUtils.deleteDirectory from Apache Commons IO. Overcomes the Java delete bug, reduces amount of code used and most of all, it works.
Instead of calling
delDir(place);
just call
FileUtils.deleteDirectory(new File(place));
Update:
In your delDir method, you call:
deleteDirectory(new File(path + "\\" + f[j]));
but the result of
File[] f = path.listFiles();
will already include the path in the file, so you can just use:
deleteDirectory( f[j].getPath() );
Not really related but:
This solution of the closing of a file helped me with another problem. When run a programme from java 6 the new process was suspended until I closed my application (in java 7 it was ok).
The solution based on this answer helped:
String[] com = new String[]{javaRun, arg1, arg2, arg3, arg4};
Process proc = Runtime.getRuntime().exec(com, null, dir);
proc = null;
System.gc();
This works with java 6. Thanks for inspiration.
The problem may be in the first line:
outFile = new FileOutputStream(dir+"\"+fileName);
Not sure the new is required.
Don't believe the directory should be included in the path. AFAIK the directory for FileOutputStream is defined as the app internal directory.
HTH
Use Case
I need to package up our kml which is in a String into a kmz response for a network link in Google Earth. I would like to also wrap up icons and such while I'm at it.
Problem
Using the implementation below I receive errors from both WinZip and Google Earth that the archive is corrupted or that the file cannot be opened respectively. The part that deviates from other examples I'd built this from are the lines where the string is added:
ZipEntry kmlZipEntry = new ZipEntry("doc.kml");
out.putNextEntry(kmlZipEntry);
out.write(kml.getBytes("UTF-8"));
Please point me in the right direction to correctly write the string so that it is in doc.xml in the resulting kmz file. I know how to write the string to a temporary file, but I would very much like to keep the operation in memory for understandability and efficiency.
private static final int BUFFER = 2048;
private static void kmz(OutputStream os, String kml)
{
try{
BufferedInputStream origin = null;
ZipOutputStream out = new ZipOutputStream(os);
out.setMethod(ZipOutputStream.DEFLATED);
byte data[] = new byte[BUFFER];
File f = new File("./icons"); //folder containing icons and such
String files[] = f.list();
if(files != null)
{
for (String file: files) {
LOGGER.info("Adding to KMZ: "+ file);
FileInputStream fi = new FileInputStream(file);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(file);
out.putNextEntry(entry);
int count;
while((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
}
ZipEntry kmlZipEntry = new ZipEntry("doc.kml");
out.putNextEntry(kmlZipEntry);
out.write(kml.getBytes("UTF-8"));
}
catch(Exception e)
{
LOGGER.error("Problem creating kmz file", e);
}
}
Bonus points for showing me how to put the supplementary files from the icons folder into a similar folder within the archive as opposed to at the same layer as the doc.kml.
Update Even when saving the string to a temp file the errors occur. Ugh.
Use Case Note The use case is for use in a web app, but the code to get the list of files won't work there. For details see how-to-access-local-files-on-server-in-jboss-application
You forgot to call close() on ZipOutputStream. Best place to call it is the finally block of the try block where it's been created.
Update: To create a folder, just prepend its name in the entry name.
ZipEntry entry = new ZipEntry("icons/" + file);