文件上传-队列多线程读取文件内容

 

队列多线程读取文件内容

为什么采用多线程队列的方式呢?主要是如果文件一行一行读取处理的话,页面上传文件后,需要等待服务器响应。如果文件的内容数据量很大的话,页面就一直等待服务器响应,毕竟服务器处理这些数据是花费很多时间的,我们都知道,页面是有响应时间的,一旦响应超时了,程序就终止了。所以,当我们需要处理大批的文件时,可以采用队列多线程的方式异步去处理,页面的响应时间只是上传文件的时间,一般在几秒之内吧。文件的数据一次性放到队列中,多个线程异步同时处理队列中的数据。

1.      场景:文件大小不是很大(十几兆以内),读取文件的内容。

 

方案:文件直接放到内存中,一行一行读取处理

 

在这里页面上传的我采用的是ajaxFileUpload插件,异步请求后台。当然,也可以用form表单的形式。

页面:

<input type="text"name="name"/>  
<input id="fileToUpload"type="file" name="file"οnchange="selectFile(this);" style="margin-left: 50px;"/>  
<button οnclick="ajaxFileUpload()">上传</button>
 
<script type="text/javascript">
 
              functionajaxFileUpload() {
                           
                            varfile = $("input[name='file']").val();
                           
                            if(typeof(file)=="undefined"||$.trim(file).length==0) {
                                          alert("请上传文件!");
                                          return;
                            }
                  $.ajaxFileUpload({
                      url : '/upload',
                      type: 'post',
                      secureuri : false,
                      fileElementId : 'fileToUpload',//input 的id
                      dataType : 'json',
                      data : {
                                  "name": name//这里随便传递了name,毕竟我们上传文件,还需要其他的一些数据
                      },
                      success : function(data) {
                                  //这里根据后台的返回数据进行处理
                      },
                      error : function(data, status, e) {
                          alert("上传发生异常");
                      }
                  })
              }            
              // 判断上传文件类型,该处设置的只允许上传txt的文件
              functionselectFile(file) {
                            varfileName = file.value;
                            varmime = fileName.toLowerCase().substr(fileName.lastIndexOf("."));
                            if(mime!=".txt"){
                                          alert("该文件不是txt文件,请重新选择!");
                                          file.outerHTML= file.outerHTML;
                            }
              }
</script>


 

 

后台:Controller

 

@SuppressWarnings("unchecked")
@ResponseBody
   @RequestMapping(value = "/upload", method =RequestMethod.POST) 
    public voidfileUpload(HttpServletRequest request, HttpServletResponse response) throwsException{ 
          Map<String,Object> result = newHashMap<String,Object>();
          result.put("success", false);
          //使用Apache文件上传组件处理文件上传步骤:
              //1、创建一个DiskFileItemFactory工厂
              DiskFileItemFactoryfactory = new DiskFileItemFactory();
              //2、创建一个文件上传解析器
                            ServletFileUploadupload = new ServletFileUpload(factory);
                            upload.setSizeMax(50*1024*1024);//设置该次上传最大值为50M
                           
                            List<FileItem>list = upload.parseRequest(request);
                              //注意:由于输入流只能读取一次,这里声明了两个文件流,inputStream用作文件读取处理,inputStreamCount用作计算文件行数,动态定义队列大小
                            InputStreaminputStream = null;
                            InputStreaminputStreamCount = null;
                            Stringname = null;
                            for(FileItemfileItem : list){
                                          //判断某项是否是普通的表单类型,否则该表单项是file 类型的
                                          if(fileItem.isFormField()){
                                                        if(fileItem.getFieldName().equals("name")){
                                                                      name= fileItem.getString("UTF-8");
                                                        }
                                          }else{
                                                        //用作-统计文件行数
                                                        inputStreamCount= fileItem.getInputStream();
                                                        inputStream= fileItem.getInputStream();
                                                        break;
                                          }
                            }
                            if(inputStream== null){
                                          log.error("文件为空!");
                        result.put("errorMsg","文件为空!");
                        ResponseUtil.writeGson(response,result);
                        return;
                            }
       BufferedReader readerLineCount =new BufferedReader(newInputStreamReader(inputStreamCount));
        //声明一个记录文件行数的变量
          int lineCount = 0;
          while(readerLineCount.readLine() !=null)//获取文件行数
          {
             lineCount++;
           }
          readerLineCount.close();
          System.out.println("文件行数:"+lineCount);
         
          //动态创建队列
          int DEFAULT_IO_THREADS = 10;
          BlockingQueue<Runnable> queue = newArrayBlockingQueue<Runnable>(lineCount);
          ThreadPoolExecutor executor = newThreadPoolExecutor(DEFAULT_IO_THREADS,
                                      DEFAULT_IO_THREADS*2,1, TimeUnit.HOURS, queue, new ThreadPoolExecutor.CallerRunsPolicy());
          String line = null;
          int i =0;
          BufferedReader reader =newBufferedReader(new InputStreamReader(inputStream));
                            while(StringUtils.isNotEmpty(line = reader.readLine())) {
                                          //处理每一行的数据
                                          send(executor,line);
                                          i++;
                            }
                            result.put("count",i);
                  result.put("success", true);
                  reader.close();
                  ResponseUtil.writeGson(response, result);
    }
   
   @SuppressWarnings("unused")
              privatevoid send(ThreadPoolExecutor executor, String line) {
         
          final String final_ String line=line;
              executor.submit(newRunnable() {
                                          publicvoid run() {
                                                        //在这里写读取到的每一行的处理逻辑
 
 
                                                       
                                          }
                            });
}


 

 

 

2.      场景:文件大(几十兆甚至更大),读取文件的内容。

 

方案:文件不能直接放到内存中,会消耗大量的内存。采用上传至服务器临时文件夹中,从文件中一行一行读取处理

页面跟例子1是一样的,主要是后台的处理方式不一样

后台:Controller

 使用MultipartFilem需要配置

<!-- 处理文件上传 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8" /> <!-- 编码 -->
        <property name="maxInMemorySize" value="10240" /> <!-- 上传时占用最大内存大小 (10240) -->
        <property name="uploadTempDir" value="/uploadTemp/" /> <!-- 上传临时保存目录名 ,带文件上传完成之后会自动删除保存的文件-->
        <property name="maxUploadSize" value="-1" /> <!-- 最大文件大小,-1为无限止(-1) -->
    </bean>



@ResponseBody
   @RequestMapping(value = "/upload", method =RequestMethod.POST) 
   public void fileUpload(@RequestParam("file") MultipartFilemultipartFile ,HttpServletRequest request, HttpServletResponse response) throwsException{ 
                 Date startTime = new Date();
                 Map<String,Object> result =new HashMap<String,Object>();
                 result.put("success",false);
                 if(multipartFile.isEmpty()){
                               log.error("文件为空!");
                               result.put("errorMsg","文件为空!");
                               ResponseUtil.writeGson(response,result);
                               return;
       }
                 String name =request.getParameter("name");//这种方式下,参数可以直接这么取
                 // 获取上传的文件保存的路径  
                 String path = request.getSession().getServletContext().getRealPath("uploadTemp");  
                 //如果目录不存在,创建文件夹 
                 if (!new File(path).exists()){   
                               newFile(path).mkdirs();
                 }
                 // 获取上传的文件的名称  
                 String filename =multipartFile.getOriginalFilename();  
                
                
                 //文件上传至指定目录下
                 File targetFile = new File(path,filename);
                 multipartFile.transferTo(targetFile);//上传
                   
                 //计算文件行数(字符流 速度最快)
                 //声明一个BufferReader对象
                 BufferedReader bReader = null;
                 //用字符流对象实例化声明的BufferReader对象
                 bReader = new BufferedReader(newFileReader(targetFile));
                 //声明一个记录文件行数的变量
                 int lineCount = 0;
                 //声明一个保存文件每行数据的String变量
                 while(bReader.readLine() != null)//获取文件行数
                 {
                    lineCount++;
                  }
                 System.out.println("文件行数:"+lineCount);
                 bReader.close();   //进行字符串与浮点类型数据转换时,先关闭刚操作的文件
                
                 int DEFAULT_IO_THREADS = 10;
                 BlockingQueue<Runnable> queue= new ArrayBlockingQueue<Runnable>(lineCount);
                 ThreadPoolExecutor executor = newThreadPoolExecutor(DEFAULT_IO_THREADS,
                                             DEFAULT_IO_THREADS*2,1, TimeUnit.HOURS, queue, new ThreadPoolExecutor.CallerRunsPolicy());
                 //通过行迭代,遍历文件的每一行,而不是把所有行都放在内存中 ,处理完之后把它扔掉。
                 LineIterator it =FileUtils.lineIterator(targetFile, "UTF-8");
                 int i=0;
                 try {
                     while (it.hasNext()) {
                         String line = it.nextLine();
                         long userId = Long.valueOf(line);
                                                 send(executor,line);
                                                 i++;
                     }
                     result.put("count", i);
                     result.put("success", true);
                     Date endTime = new Date();
                     System.out.println("耗时:"+(endTime.getTime()-startTime.getTime())+"毫秒");
                     //删除临时文件
                     LineIterator.closeQuietly(it);
                     targetFile.delete();
                     ResponseUtil.writeGson(response, result);
                 } catch (Exception e){
                               result.put("errorMsg",e.getMessage());
                               ResponseUtil.writeGson(response,result);
                 }
                 finally {
                     LineIterator.closeQuietly(it);
                 }
    }
   
   @SuppressWarnings("unused")
       privatevoid send(ThreadPoolExecutor executor,String line) {
                 final String line = line;
                     executor.submit(newRunnable() {
                                   publicvoid run() {
                                                 //在这里写处理逻辑
                                   }
                     });
  }


补充:像这种文件上传的,一定要防止重复提交。所以,防重复提交必须防止。

方案: 1.前端,js点击提交后,立刻移除点击事件。

             2.后端,加锁。private Map<String, String> objLock = new ConcurrentHashMap<String, String>();

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值