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

来源:互联网 发布:json遍历放到input中 编辑:程序博客网 时间:2024/05/10 17:42

 

队列多线程读取文件内容

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

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

 

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

 

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

页面:

<input type="text"name="name"/>  <input id="fileToUpload"type="file" name="file"onchange="selectFile(this);" style="margin-left: 50px;"/>  <button onclick="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>();

1 0
原创粉丝点击