commons-fileupload and jakarta.servlet

5.9k views Asked by At

We are currently in the process of migrating our application to Spring Boot 3. We have been using the Commons FileUpload streaming API to directly load large multipart files into a remote file share. However, we have encountered an issue where the javax.servlet.http.HttpServletRequest has been migrated to jakarta.servlet.http.HttpServletRequest. As a result, the FileUpload library is no longer functioning since it requires javax.servlet.http.HttpServletRequest.

Does Servlet API or Spring framework provide a similar functionality for file streaming without the need to save all files in memory beforehand?

2

There are 2 answers

2
odinx On BEST ANSWER

Apache is planning to release a new version of FileUpload that supports Jakarta, which should resolve the issues with migrating from javax to jakarta.

Please note that the dependency declaration will be changed for the new version. You can refer to the previous link for more information:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-fileupload2-jakarta</artifactId>
  <version>2.0.0</version>
</dependency>
0
noo On

You can use context instead of request and create your own context like this (code is Kotlin but you can do the same with Java):

import jakarta.servlet.http.HttpServletRequest
import org.apache.commons.fileupload.FileUploadBase
import org.apache.commons.fileupload.UploadContext
import java.io.InputStream

class JakartaServletRequestContext(val request: HttpServletRequest): UploadContext {


    override fun getCharacterEncoding(): String {
        return request.characterEncoding
    }

    override fun getContentType(): String {
        return request.contentType
    }

    override fun getContentLength(): Int {
        return request.contentLength
    }

    override fun getInputStream(): InputStream {
        return request.inputStream
    }

    override fun contentLength(): Long {
        return try {
            request.getHeader(FileUploadBase.CONTENT_LENGTH).toLong()
        } catch (e: NumberFormatException) {
            request.contentLength.toLong()
        }
    }
}

and then use it like

    @PostMapping("/send")
    fun handleIn(request: HttpServletRequest): String {
        val context = JakartaServletRequestContext(request)

        if (!ServletFileUpload.isMultipartContent(context)) {
            throw RuntimeException("Multipart request expected")
        }


        fileService.upload(ServletFileUpload().getItemIterator(context))

        return "OK"
    }

and the code for the file service could be:

class FileService {

    val saveDir = File("/tmp/in")

    @Throws(IOException::class)
    fun upload(iter: FileItemIterator) {
        while (iter.hasNext()) {
            val item = iter.next()
            if (item.isFormField) {
                continue
            }
            upload(item)
        }
    }


    @Throws(IOException::class)
    private fun upload(item: FileItemStream) {
        val fileName = item.name
        val type = item.contentType
        val ins = item.openStream()
        val destination = File(saveDir, fileName)
        val outs = FileOutputStream(destination)
        IOUtils.copy(ins, outs)
        IOUtils.closeQuietly(ins)
        IOUtils.closeQuietly(outs)
        println("Saved $fileName with type $type to $destination")
    }
}