Lms24
Lms24

Reputation: 722

Webapplication: Delete files during runtime

I am developing a webapp (for mobile phones). There is one xhtml page, where I want to show a picture, which is stored locally on my hard drive (for example: D:\pictures\test.jpg). Since browsers block images when they are located on a local harddrive, I wrote a method in my javabean, where the picture, stored on the localHD, is copied to the webApp directory, when the user enters the xhtml page. After the user leaves the page, the copied file inside the webapp should be deleted. So when I'm running my app, copying works perfectly and the pictures are displayed correctly. However, when the files should get deleted, I get this errormessage:

java.nio.file.FileSystemException: D:\WebAppPath\src\main\webapp\resources\pics\test.jpg: The process cannot be accessed because the file is being used by another process.

Strangely enough, after stopping and restarting the application I can delete the same image if it is still in the webApp directory. (But Only once; after re-copying it, I get the error message again.)

Also if I want to delete the file manually, by using Windows explorer, I get the error message that the file can't be deleted because it is used by Java(TM) Platform SE Binary.

So to delete the file (manually or via the bean) I have to wait for a restart of the application, which of course is not an acceptable solution for the end user.

I'm using JSF2.0 with Primefaces and Primefaces Mobile components. My IDE is Netbeans and I use Spring Webflow framework to navigate and trigger actions/methods between the xhtml pages.

Here's the code for the copying method in my JavaBean:

    public void copyFotoToLocalhost() {
    if (fotoList.size() > 0) {
        for (int i = 0; i < fotoList.size(); i++) {
            Foto tempPic = fotoList.get(i);
            String tempItemName = tempPic.getItemName();
            String originalFile = "D:\\localFilepath\\" + tempItemName;
            String tempFileName = "D:\\WebAppPath\\src\\main\\webapp\\resources\\pics\\" + tempItemName;
            File existTest = new File(tempFileName);

            if (existTest.exists() == false) {
                try {
                    File orFile = new File(originalFile);
                    File tempFile = new File(tempFileName);


                    InputStream in = new FileInputStream(orFile);
                    OutputStream out = new FileOutputStream(tempFile);

                    byte[] buf = new byte[8192];
                    int len;
                    while ((len = in.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    in.close();
                    out.close();

                    tempFile.setWritable(true);

                    System.out.println("File copied.");
                } catch (FileNotFoundException ex) {
                    System.out.println(ex.getMessage() + " in the specified directory.");
                    System.exit(0);
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }
}

Here's the code for the delete method:

public void deleteFotos() {
        if (fotoList.size() > 0) {
            for (int i = 0; i < fotoList.size(); i++) {
                Foto tempPic = fotoList.get(i);
                String tempItemName = tempPic.getItemName();

                Path tempLocation = Paths.get("D:\\webAppPath\\src\\main\\webapp\\resources\\pics\\" + tempItemName);
                fotoList.remove(i);
                i--;
                try {
                    Files.deleteIfExists(tempLocation);
                    System.out.println("sucessfully deleted" + tempPic.getItemName());
                } catch (IOException ex) {
                    Logger.getLogger(WundDokuBean.class.getName()).log(Level.SEVERE, null, ex);
                    System.out.println("Fail @ " + tempPic.getItemName());
                }

            }
            fotoList.clear();
        }

Do you have an idea, how to fix this?

I hope you understand my problem, if not please tell me which information you need, I'll try to provide it.

Upvotes: 1

Views: 1490

Answers (1)

BalusC
BalusC

Reputation: 1109122

There is one xhtml page, where I want to show a picture, which is stored locally on my hard drive (for example: D:\pictures\test.jpg). Since browsers block images when they are located on a local harddrive (...)

I want to clear out a conceptual misunderstanding first: You seem to expect that it would work fine when the browser wouldn't have blocked it. This is completely untrue. You seem to expect that images are inlined in the HTML output. No, they are downloaded individually and independently from the HTML page. If you had continued to use local disk file system paths, then it would have worked only and only if your webpage visitor has also exactly the same file at exactly the same location at their disk file system. In reality, this is obviously not the case. It would only work if both the webbrowser and webserver runs at physically the same machine.


Coming back to your concrete problem of being unable to delete the file, it's is caused because the servletcontainer usually locks the files in expanded WAR folder. I can't tell the exact reason, but that's not relevant here as this whole approach is wrong anyway. This approach would fail when the deployed WAR file is not expanded on disk file system, but instead in server's memory. Also, hardcoding environment-specific disk file system paths is a bad idea. You'd need to edit, rewrite, recompile, rebuild the whole WAR everytime you change the environment. In other words, your webapp is not portable.

You need to keep the files there where they originally are and make them publicly available by a real URL. This can be achieved in 2 general ways:

  1. Add a virtual host to the server config, pointing to D:\localFilepath\. How to achieve that depends on the server used. You didn't tell anything about the server make/version used, but using Spring suggests that you're not being able to use full Java EE stack and are likely using a barebones JSP/Servlet container such as Tomcat. In that case, it's a matter of adding the following line to its /conf/server.xml:

    <Context docBase="D:\localFilepath" path="/fotos" />
    

    This way they are available by http://localhost:8080/fotos/*.


  2. Create a servlet which reads files from D:\localFilepath and writes to HTTP response. With Servlet 3.0 and Java 7 it's really a breeze. Here's a kickoff example (nullchecks/file-exist-checks/doHead()/caching/resuming omitted for brevity):

    @WebServlet("/fotos/*")
    public class FotosServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletExcpetion, IOException {
            File file = new File("D:/localFilepath", request.getPathInfo().substring(1));
            response.setHeader("Content-Type", getServletContext().getMimeType(file.getName()));
            response.setHeader("Content-Length", String.valueOf(file.length()));
            Files.copy(file.toPath(), response.getOutputStream());
        }
    
    }
    

    That's basically it. This way they're available on http://localhost:8080/contextname/fotos/*.

Upvotes: 1

Related Questions