0

I am using spring MVC where through API I am uploading zip file using MultipartFile. In backend I have to convert uploaded zip file into InputStream for further processing. But my code is giving error intermittently " File has been moved, can not be read again ".

here is the code snippet :

            File temp = null;
            InputStream stream = null;
            try {
                InputStream initialStream = inputFile.getInputStream();
                byte[] buffer = new byte[initialStream.available()];
                initialStream.read(buffer);
                temp = File.createTempFile("upload", null);
                try (OutputStream outStream = new FileOutputStream(temp)) {
                    outStream.write(buffer);
                }
                ZipFile zipFile = new ZipFile(temp);
                stream = zipFile.getInputStream(zipFile.getEntries().nextElement());
            } catch (Exception e) {
                log.error("Exception occurred while processing zip file " + e.getMessage());
                throw e;
            } finally {
                if (temp != null)
                    temp.delete();
            }
            return stream;

Here inputFile is MultipartFile.

Could you please suggest what is wrong here?

2 Answers2

1

Your code is returning an input stream from a file that you have deleted - last line is temp.delete().

ZipInputStream has a small internal buffer for decoding, so that may explain why some read calls work after the delete, but it will not be possible to continue reading from a file that you deleted, hence the exception.

Also, the call initialStream.available() is unlikely to be the correct way to determine the size of the input stream file part. Try printing the size / check how to read the actual length of the file in the multipart stream - such as part.getSize(), or transfer the bytes into a new ByteArrayOutputStream() before assigning to buffer.

I would not recommend doing any work with files or multipart streams using direct transfer to byte[] as you risk OutOfMemoryException. However in your case where you are happy to have byte[] for the ZIP and you read the first entry of the ZIP file (and are ignoring other entries) then you could try extracting the first entry as InputStream without writing to a file as follows:

// Read a zip input stream from a zip stored in byte[]:
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(buffer));
// Select first entry from ZIP
ZipEntry entry = zis.getNextEntry();

// You should be able to read the entry from zis directly,
// if this is text file you could test with:
//    zis.transferTo(System.out);

return zis;

You should ensure that you close the stream after use.

DuncG
  • 12,137
  • 2
  • 21
  • 33
  • while debugging i found that below line is throwing exception. InputStream initialStream = inputFile.getInputStream(); – Avinash kumar Oct 03 '22 at 10:47
  • @Avinash kumar I suggest you edit your question with details of what error you actually get - including the code for `inputFile.getInputStream()` especially if that is where the error is rather than in the unzip section. – DuncG Oct 03 '22 at 13:26
  • I tried with removing temp.delete() but it issue is there. Currently i am not facing issue with small size file but with file ~10MB size getting error occasionally. can I do with configuring maxInMemorySize? – Avinash kumar Oct 04 '22 at 05:02
  • I don't think the error is in unzip section. Also inputFile.getInputStream() is method of MultiPartFile. (here inputFile is instance of MultiPartFile). Basically in debug i found that it calls isAvailable() and there it goes into DiskFileItem check section but sometime it doesn't find file in stored location hence the error – Avinash kumar Oct 04 '22 at 05:08
  • See my edit, `initialStream.available()` is unlikely to be the correct way to determine the size of the input stream file part. – DuncG Oct 04 '22 at 07:02
0

Potential issues I can see in your code:

  1. temp file is used as zip file, yet you delete the temp file prior to returning. How can you use the zip file as file stream if you have deleted it?

  2. Do you support concurrent uploads? If yes, then you have concurrent resource access problem. Multiple calls to create temp file: "upload" and process it. Why don't you create a different filename e.g. with datetime suffix + random number suffix.

fauzimh
  • 594
  • 4
  • 16
  • See javadoc for `File.createTempFile`, it generates a new filename each time so point 2 is not relevant. – DuncG Oct 03 '22 at 09:03