I have a rest service in Java EE and for some weird backward compatibility reasons I have to return an .mdb file from a URL request after adding some rows inside it.
At first I simply opened an mdb file, cleared all rows in it, wrote my rows and returned it to the caller, however I realized the .mdb kept growing this way because Access doesn't purge the rows on deletion but only erases it and the library I am using (Jackcess) doesn't support purging completely the rows.
So I switched to creating a copy of an empty .mdb file with java.io.File.createTempFile() and returning it however a dangling pointer to the file in the /tmp/ folder is left and after several days I get a
java.io.FileNotFoundException: /tmp/tmpdb.mdb04949499 (Too many open files)
The only solutions I found so far are:
Set MAX_FILE_HANDLES_FOR_READ_ENDS_MAP to a very high number (which only postpones the problem)
deleting the temp file, which however is not viable because I return it from a function and once returned I lose control of the pointer.
below what I currently have:
GET
#Path("/get/database/{filename}")
#Produces("application/jet")
public StreamingOutput getDatabase(#PathParam("filename") String fileName)
{
//access files increase indefinitely in size because deleted rows are simply marked "deleted" and not removed
//so we create a temporary file equal to the template .mdb file and use it instead
java.io.File myDBFile = null;
try
{
java.io.File templateDBFile = new java.io.File(url + "resources/clean_tmpdb.mdb");
myDBFile = java.io.File.createTempFile("tmpdb", ".mdb");
myDBFile.deleteOnExit(); //useless hint
FileChannel src = new FileInputStream(templateDBFile).getChannel();
FileChannel dest = new FileOutputStream(myDBFile).getChannel();
dest.transferFrom(src, 0, src.size());
}
catch (IOException ex)
{
Logger.getLogger(FileResource.class.getName()).log(Level.SEVERE, null, ex);
}
finally
{
if (src != null)
{
try
{
src.close();
}
catch (IOException ex)
{
Logger.getLogger(FileResource.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (dest != null)
{
try
{
dest.close();
}
catch (IOException ex)
{
Logger.getLogger(FileResource.class.getName()).log(Level.SEVERE, null, ex);
}
}
/* work on the file inserting rows */
return new FileStreamingOutput(myDBFile);
}
EDIT: found a similar question, with a vague accepted answer: How to delete file after REST response, the accepted answer is "just write directly to the output stream contained in the Response."
You aren't closing either src or dest. So as to ensure they are closed, close them in finally blocks, or use the try-with-resources syntax.
return new FileStreamingOutput(myDBFile);
/* here I should call myDBFile.close(); and myDBFile.delete(); */
Here you can't call myDBFile.close(), as there is no such method. You can't call myDBFile.delete() either, as otherwise the caller will receive a File that doesn't exist. If the File needs to be deleted, the caller will have to do it. Your stated requirement doesn't make sense.
Related
I tried downloading a file attachment using a variant of the example code given by the official JDA documentation. Afterwards, the downloaded file should be moved to another place.
List<Message.Attachment> attachments = null;
try {
attachments = event.getMessage().getAttachments();
} catch (UnsupportedOperationException ignore) {}
File downloadFile;
if (attachments != null && !attachments.isEmpty()) {
Message.Attachment attachment = attachments.get(0);
downloadFile = new File("./tmp/testfile");
downloadFile.getParentFile().mkdirs();
attachment.downloadToFile(downloadFile)
.thenAccept(file -> System.out.println("Saved attachment"))
.exceptionally(t -> {
t.printStackTrace();
return null;
});
}
...
File renamedFile = new File("./files/movedfiled");
renamedFile.getParentFile().mkdirs();
try {
Files.move(downloadFile.toPath(), renamedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
I already tried to add .complete(Void) after .exceptionally(...) and .complete(File) after .downloadToFile(File). None of these worked.
Most of the time, the moved file has a size of 0 bytes or does not exist at all and the original is still present in the old directory (sometimes the size of the downloaded one is also 0 bytes).
Is there a way to wait for completion of the download and closure after writing to prevent file corruption while moving or is the problem caused by my file system (I'm using an aarch64 GNU/Linux system)?
Message.Attachment#downloadToFile() returns a CompletableFuture. You can use CompletableFuture#join() to wait until it finishes but IIRC this is a blocking action.
Better use CompletableFuture#thenAccept() or CompletableFuture#thenCompose().
attachment.downloadToFile(downloadFile)
.thenAccept(file -> {
// Here goes the code which decides what to do after downloading the file
})
.exceptionally(e -> {
e.printStackTrace();
return null;
});
Getting an error when trying to open a FileInputStream to load Map from file with .ser extension.
Constructor where I create new File and invoke method that loads map from file:
protected DriveatorImpl() {
accounts = new ConcurrentHashMap<String, Client>();
db = new File("database.ser"); // oddly this does not create a file if one does not exist
loadDB();
}
#SuppressWarnings("unchecked")
private void loadDB() {
try {
fileIn = new FileInputStream(db);
in = new ObjectInputStream(fileIn);
accounts = (Map<String, Client>) in.readObject();
in.close();
fileIn.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
I've tried to create file manually and put it in same package with class, but it does not help. What's going on?!
Thank You!
You provide a relative path for the file. That means program will look for the file relative to the working directory.
Depending on how you run the program it will be the directory you run it from (if run from Shell/Cmd) or whatever is configured in the project settings (if run from the IDE). For the latter, it depends on the IDE but usually it's the project root directory.
More info on working directory: https://en.wikipedia.org/wiki/Working_directory
More info on relative path: https://en.wikipedia.org/wiki/Path_(computing)#Absolute_and_relative_paths
Regarding creation of the file, it would create non-existing file if you were to write to it. When you read it, it expects it to exist. That means you have to create empty file (if one does not exist) before reading or simply treat exception as empty content.
The path to the file you have given might be wrong for IDE it can take relative path but from the command line, it will take the absolute path.
I am trying to transfer the data from old textfile to new textfile. Although the code below is able to transfer successfully, it does not delete the old textfile. May I know why is this so?
private void dataTransfer(String oldFilePath, String newFilePath) {
byte[] buffer = new byte[10000];
try {
FileInputStream fileInput = new FileInputStream(oldFilePath);
BufferedInputStream bufferedInput = new BufferedInputStream(fileInput);
FileOutputStream fileOutput = new FileOutputStream(newFilePath);
BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutput);
while(true) {
int length = fileInput.read(buffer);
if(length == -1) {
break;
} else {
bufferedOutput.write(buffer);
bufferedOutput.flush();
}
}
fileInput.close();
bufferedInput.close();
fileOutput.close();
bufferedOutput.close();
File oldFile = new File(oldFilePath);
oldFile.delete();
} catch (IOException e) {
e.printStackTrace();
System.out.println(ERROR_TRANSFER_DATA);
}
}
Update the JRE and JDK, make sure you have the rights on the file. Try with a file created by you.
Also, add a catch block for SecurityException
For deleting a file it should work fine but for deleting a directory you have to make sure that Directory is Empty.
You can use the following code block. It works, though don't know. Even without setWritable, it works,
oldFile.setWritable(true);
if(!oldFile.delete()){
System.out.println("de;eted");
}
According to Oracle's docs, the delete method does not guarantee that it will delete the file.
http://docs.oracle.com/javase/7/docs/api/java/io/File.html#delete()
Deleting a file will fail if:
file does not exist
there is a lock on that file it might be opened by another process
file does not exist on the disk
you don't have enough permissions to delete that file (in this case a SecurityException is thrown)
I agree with #panagdu that you might not have sufficient rights to delete the file.
Just as a fluke try closing bufferedStream before fileInputStream
like
bufferedInput.close();
fileInput.close();
bufferedOutput.close();
fileOutput.close();
But I don't think this will help.
Test your code for files with sufficient permission. For example Java does not allow the delete() for system files.
I have a text file which I downloaded from the internet. File is large, somewhat around 77MB and I need to map it into the memory so I can read it fast. Here is my code
public class MapRead {
public MapRead()
{
try {
File file = new File("E:/Amazon HashFile/Hash.txt");
FileChannel c = new RandomAccessFile(file,"r").getChannel();
MappedByteBuffer buffer = c.map(FileChannel.MapMode.READ_ONLY, 0,c.size()).load();
System.out.println(buffer.isLoaded());
System.out.println(buffer.capacity());
} catch (IOException ex) {
Logger.getLogger(MapRead.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
No good, this generated the below output.
false
81022554
This is my first time trying this. I have no idea what went wrong, or what to do next, to read the file.
So I followed the block of code here: http://commons.apache.org/proper/commons-compress/examples.html, where is said to simply make a ZipArchiveEntry and then insert the data. As you can see by my code below.
public void insertFile(File apkFile, File insert, String method)
throws AndrolibException {
ZipArchiveOutputStream out = null;
ZipArchiveEntry entry;
try {
byte[] data = Files.toByteArray(insert);
out = new ZipArchiveOutputStream(new FileOutputStream(apkFile, true));
out.setMethod(Integer.parseInt(method));
CRC32 crc = new CRC32();
crc.update(data);
entry = new ZipArchiveEntry(insert.getName());
entry.setSize(data.length);
entry.setTime(insert.lastModified());
entry.setCrc(crc.getValue());
out.putArchiveEntry(entry);
out.write(data);
out.closeArchiveEntry();
out.close();
} catch (FileNotFoundException ex) {
throw new AndrolibException(ex);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
Basically, its passed the File (apkFile) that will take the "insert" File, with another parameter dictating the compression method of that file. Running this block of code results in 0 errors, but the ZIP file only has that "new" file in it. It removes all the previous files and then inserts that new one.
Prior to commons-compresss, I had to copy the entire Zip to a temporary file, do my changes, and then copy that finalized Zip file back. I thought this library worked around that though?
You always want to close() streams when you are done with them (i.e. out.close()), preferably in a finally block.
As an alternative: I've written some utility methods to copy files and directories to a Zip file using the NIO.2 File API (the library is Open Source):
Maven:
<dependency>
<groupId>org.softsmithy.lib</groupId>
<artifactId>softsmithy-lib-core</artifactId>
<version>0.3</version>
</dependency>
Tutorial:
http://softsmithy.sourceforge.net/lib/current/docs/tutorial/nio-file/index.html#AddZipResourceSample
API: CopyFileVisitor.copy