代码之家  ›  专栏  ›  技术社区  ›  Kaliyug Antagonist

以非阻塞方式处理文件上传

  •  0
  • Kaliyug Antagonist  · 技术社区  · 13 年前

    背景线程是 here

    只是为了明确目标——用户将上传一个大文件,必须立即重定向到另一个页面以进行不同的操作。但是文件太大,从控制器的 输入流 。所以我不情愿地决定用一个新的线程来处理这个I/O。代码如下:

    控制器servlet

    /**
         * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
         *      response)
         */
        protected void doPost(HttpServletRequest request,
                HttpServletResponse response) throws ServletException, IOException {
            // TODO Auto-generated method stub
    
            System.out.println("In Controller.doPost(...)");
    
            TempModel tempModel = new TempModel();
            tempModel.uploadSegYFile(request, response);
    
            System.out.println("Forwarding to Accepted.jsp");
    
            /*try {
                Thread.sleep(1000 * 60);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }*/
    
            request.getRequestDispatcher("/jsp/Accepted.jsp").forward(request,
                    response);
        }
    

    模型类

    package com.model;
    
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.utils.ProcessUtils;
    
    public class TempModel {
    
        public void uploadSegYFile(HttpServletRequest request,
                HttpServletResponse response) {
            // TODO Auto-generated method stub
    
            System.out.println("In TempModel.uploadSegYFile(...)");
    
            /*
             * Trigger the upload/processing code in a thread, return immediately
             * and notify when the thread completes
             */
            try {
                FileUploaderRunnable fileUploadRunnable = new FileUploaderRunnable(
                        request.getInputStream());
    
                /*
                 * Future<FileUploaderRunnable> future = ProcessUtils.submitTask(
                 * fileUploadRunnable, fileUploadRunnable);
                 * 
                 * FileUploaderRunnable processed = future.get();
                 * 
                 * System.out.println("Is file uploaded : " +
                 * processed.isFileUploaded());
                 */
    
                Thread uploadThread = new Thread(fileUploadRunnable);
                uploadThread.start();
    
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } /*
             * catch (InterruptedException e) { // TODO Auto-generated catch block
             * e.printStackTrace(); } catch (ExecutionException e) { // TODO
             * Auto-generated catch block e.printStackTrace(); }
             */
    
            System.out.println("Returning from TempModel.uploadSegYFile(...)");
        }
    
    }
    

    这个 可跑步

    package com.model;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.ByteBuffer;
    import java.nio.channels.Channels;
    import java.nio.channels.ReadableByteChannel;
    
    public class FileUploaderRunnable implements Runnable {
    
        private boolean isFileUploaded = false;
        private InputStream inputStream = null;
    
        public FileUploaderRunnable(InputStream inputStream) {
            // TODO Auto-generated constructor stub
            this.inputStream = inputStream;
        }
    
        public void run() {
            // TODO Auto-generated method stub
    
            /* Read from InputStream. If success, set isFileUploaded = true */
            System.out.println("Starting upload in a thread");
    
            File outputFile = new File("D:/06c01_output.seg");/*
                                                             * This will be changed
                                                             * later
                                                             */
            FileOutputStream fos;
            ReadableByteChannel readable = Channels.newChannel(inputStream);
            ByteBuffer buffer = ByteBuffer.allocate(1000000);
    
            try {
    
                fos = new FileOutputStream(outputFile);
    
                while (readable.read(buffer) != -1) {
                    fos.write(buffer.array());
                    buffer.clear();
                }
    
                fos.flush();
                fos.close();
    
                readable.close();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            System.out.println("File upload thread completed");
        }
    
        public boolean isFileUploaded() {
            return isFileUploaded;
        }
    
    }
    

    我的疑问:

    1. 从Servlet手动生成线程在逻辑上对我来说是有意义的,但在编码方面让我感到害怕——容器毕竟不知道这些线程(我想是的!)
    2. 当前代码给出了一个非常明显的异常- 流是不可访问的,因为doPost(…)方法在run()方法完成之前返回 以下为:

      In Controller.doPost(...)
      In TempModel.uploadSegYFile(...)
      Returning from TempModel.uploadSegYFile(...)
      Forwarding to Accepted.jsp
      Starting upload in a thread
      Exception in thread "Thread-4" java.lang.NullPointerException
          at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:512)
          at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:497)
          at org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:559)
          at org.apache.coyote.http11.AbstractInputBuffer.doRead(AbstractInputBuffer.java:324)
          at org.apache.coyote.Request.doRead(Request.java:422)
          at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.java:287)
          at org.apache.tomcat.util.buf.ByteChunk.substract(ByteChunk.java:407)
          at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:310)
          at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:202)
          at java.nio.channels.Channels$ReadableByteChannelImpl.read(Unknown Source)
          at com.model.FileUploaderRunnable.run(FileUploaderRunnable.java:39)
          at java.lang.Thread.run(Unknown Source)
      
    3. 记住这一点1.无论如何,Executitor框架的使用对我有帮助吗?

      package com.utils;
      
      import java.util.concurrent.Future;
      import java.util.concurrent.ScheduledThreadPoolExecutor;
      
      public final class ProcessUtils {
      
          /* Ensure that no more than 2 uploads,processing req. are allowed */
          private static final ScheduledThreadPoolExecutor threadPoolExec = new ScheduledThreadPoolExecutor(
                  2);
      
          public static <T> Future<T> submitTask(Runnable task, T result) {
      
              return threadPoolExec.submit(task, result);
          }
      }
      

    那么我该如何确保 用户不会阻止,并且流仍然可以访问,因此可以从中读取(上传的)文件 ?

    1 回复  |  直到 8 年前
        1
  •  2
  •   jdevelop    13 年前

    实际上并没有。您正在尝试生成线程并读取POST请求的内容,还试图将用户转发到具有相同请求对象的另一个页面。这混淆了servlet容器。

    你可以

    • 使用单独的框架上传表单和控制器
    • 使用带有上传表单和独立控制器的弹出窗口
    • 使用AJAX加载下一个页面,同时仍然上传当前页面中的内容(让浏览器为您处理)