📜 ⬆️ ⬇️

Using apr_socket_sendfile () from servlets under Tomcat

In this topic I will talk about a small but effective way to transfer files to a user from a servlet using an HTTP protocol. Used by:

Of course, giving files to a servlet is not very good in terms of performance. First, it is best to give static content without any scripts at all. But sometimes you can't do without it. Secondly, the return of data comes down, most often, to something like this:
long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  1. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  2. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  3. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  4. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  5. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  6. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  7. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
  8. long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .
long writed = 0; byte [] buffer = new byte [BUFFER_LENGTH]; int readed = in .read(buffer, 0, BUFFER_LENGTH); while (readed != -1) { out .write(buffer, 0, readed); writed += readed; readed = in .read(buffer, 0, BUFFER_LENGTH); } * This source code was highlighted with Source Code Highlighter .

After reading the books on NIO and using a microscope, you can transform it into a slightly more effective tool:
  1. public static long transfer (File file, OutputStream out ) throws IOException {
  2. return transfer (file, 0, file.length (), out );
  3. }
  4. public static long transfer (File file, long position, long count,
  5. OutputStream out ) throws IOException {
  6. FileChannel in = new FileInputStream (file) .getChannel ();
  7. try {
  8. long writed = in .transferTo (position, count, Channels
  9. .newChannel ( out ));
  10. return writed;
  11. } finally {
  12. in .close ();
  13. }
  14. }
* This source code was highlighted with Source Code Highlighter .

However, those who carefully studied the trace trace of their server know that Tomcat's OutputStream does not support channeling, and it all comes down to the first example, but already in the depths of the JVM.

Obvious disadvantages of this approach:

However, Apache Tomcat allows servlets to use (via a convenient interface) the apr_socket_sendfile function from the Apache Portable Runtime library. This function takes as input a pointer to the socket, to the file, as well as the start and length parameters of the transmitted data (you can transfer not only the entire file). Access to this functionality is done through the use of request attributes (HttpServletRequest). Check availability of this functionality:
  1. private static final String TOMCAT_SENDFILE_SUPPORT = "org.apache.tomcat.sendfile.support" ;
  2. final boolean sendFileSupport = Boolean.TRUE.equals (request
  3. .getAttribute (TOMCAT_SENDFILE_SUPPORT));
* This source code was highlighted with Source Code Highlighter .


Now, if:
  1. sendFileSupport == true
  2. The file will not be deleted immediately after the execution of the code.
  3. File size is less than 2 GB

Instead of transferring the file yourself, you can use Apache Tomcat:
  1. private static final String TOMCAT_SENDFILE_FILENAME = "org.apache.tomcat.sendfile.filename" ;
  2. private static final String TOMCAT_SENDFILE_START = "org.apache.tomcat.sendfile.start" ;
  3. private static final String TOMCAT_SENDFILE_END = "org.apache.tomcat.sendfile.end" ;
  4. // using Apache APR and / or NIO to transfer file
  5. response.setBufferSize (1 << 18);
  6. request.setAttribute (TOMCAT_SENDFILE_FILENAME, file.getCanonicalPath ());
  7. request.setAttribute (TOMCAT_SENDFILE_START, Long.valueOf (0));
  8. request.setAttribute (TOMCAT_SENDFILE_END, Long.valueOf (fileLength));
* This source code was highlighted with Source Code Highlighter .

The second limitation is due to the fact that the file transfer process will be started after we finish the work in the servlet. Thirdly, it’s not clear what, but perhaps with the fact that I have a 32-bit JVM and a 32-bit Gentoo on a test machine (Tomcat did not want to give the file to more than 2 Gb itself).
')
As a result:

Of course, for the production system you need not only to be able to give the entire file, but also in parts, and also to take into account the possibility that the user already has the file (to process NotModifiedSince).

For further study

Source: https://habr.com/ru/post/92756/


All Articles