`
youbin_
  • 浏览: 5981 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

WEB多线程Socket文件上传

阅读更多

郁闷,第一次发个帖就被扣10分,呜呜……
不过我脸皮厚,又来了。

这次的问题是我在WEB应用中要实现“大”(几百兆)文件的上传,如果用IE上传,肯定Over!我用有证书的Applet在客户端用多线程通过socket将文件上传。在本机上测试好像可以,但是通过网络上传时就会发生掉包现象,必须重新上传。虽然这是程序自动的,但这样也会浪费大量的时间,有时会重复好几次。我检查了好几次代码,也没有发现什么问题!不知道是不是socket的传输不可靠呢,还是我的代码本身就有问题呢?下面是我的试验代码中主要的2个类,请大家帮我看看是否有什么地方不妥的,谢谢!!

第一个:文件上传处理程序,处理文件上传逻辑

package test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

/**
 * $Id: UploadHandler.java,v 1.1 2007/05/14 06:11:02 zhangyb Exp $
 *
 * 描述: 文件上传处理程序,处理文件上传逻辑
 * 
 *
 * @author zhangyb
 * @version 1.0
 */
public class UploadHandler extends Thread {

 /** 系统日志 */
 // private final Log logger = LogFactory.getLog(UploadHandler.class);
 /** 最大文件块大小 20M */
 public final static long MAX_FILE_BLOCK_SIZE = 10 * 1024 * 1024;

 /** 最大缓存大小 1M */
 public final static int MAX_BUFFER_SIZE = 1024 * 1024;

 /** 文件上传的最大线程数 */
 public final static int MAX_THREADS = 5;

 /** 文件系统中的文件 */
 private java.io.File realFile;

 /** 文件的块数 */
 private int blocks;

 /** 当前上传的块序号 */
 private int currentBlock = 1;

 /** 最后块的大小 */
 private long lastLength;

 /** 正在上传的线程数 */
 private int threads = 0;

 /** 文件的唯一编号 */
 private String fileCode = "aaaaaaaaaaaaaaaaaaaa";

 /** 远程主机 */
 private String host;

 /** 远程主机监听端口 */
 private int port;

 private JProgressBar progressBar = null;

 private ProgressDialog dialog = null;

 private FileUpload applet = null;

 /**
  * When the worker needs to update the GUI we do so by queuing a Runnable for the event dispatching thread with SwingUtilities.invokeLater(). In this case we're just changing the progress bars value.
  */
 public void updateStatus(final int i) {

  Runnable doSetProgressBarValue = new Runnable() {
   public void run() {
    progressBar.setValue(i);
   }
  };
  SwingUtilities.invokeLater(doSetProgressBarValue);
 }

 public UploadHandler(String host, int port, java.io.File file) {
  // logger.info("开始上传文件" + file.getName() + "...");
  this.host = host;
  this.port = port;
  this.realFile = file;
  start();
 }

 public UploadHandler(String host, int port, java.io.File file, ProgressDialog dialog, FileUpload applet) {
  // logger.info("开始上传文件" + file.getName() + "...");
  this.host = host;
  this.port = port;
  this.realFile = file;
  this.dialog = dialog;
  this.progressBar = dialog.getProgressBar();
  this.applet = applet;
  start();
 }

 public void run() {

  if (realFile.isDirectory()) {
   // 文件夹情况
   java.io.File[] files = realFile.listFiles();
   if (files != null || files.length > 0) {
    for (int i = 0; i < files.length; i++) {
     new UploadHandler(host, port, files[i]);
    }
   }
  } else {
   // 文件情况, 上传文件
   try {
    RandomAccessFile randomAccessFile = new RandomAccessFile(realFile, "r");
    long size = randomAccessFile.length();
    randomAccessFile.close();
    lastLength = size % MAX_FILE_BLOCK_SIZE;
    if (lastLength == 0) {
     blocks = (int) (size / MAX_FILE_BLOCK_SIZE);
    } else {
     blocks = (int) (size / MAX_FILE_BLOCK_SIZE) + 1;
    }
    // progressBar.setMaximum((int) size);
    progressBar.setValue(0);
    progressBar.setStringPainted(true);
    if (blocks >= MAX_THREADS) {
     threads = MAX_THREADS;
    } else {
     threads = blocks;
    }
    for (int i = 0; i < threads; i++) {
     new BlockSender(currentBlock);
     currentBlock += 1;
    }
   } catch (FileNotFoundException e) {
    // logger.error("文件" + file.getFileCode() + "未能找到, 不能完成上传.");
    reset();
   } catch (IOException e) {
    // logger.error("发生文件或网络读写错误, 不能完成上传.");
    reset();
   }
  }
 }

 /**
  * 文件块上传类
  */
 class BlockSender extends Thread {

  int block;

  Socket socket;

  BlockSender(int block) {
   this.block = block;
   try {
    // socket = new Socket(host, port);
    socket = new Socket("localhost", 5000);
    start();
   } catch (UnknownHostException e) {
    e.printStackTrace();
    System.out.println("未能找到远程主机, 不能完成上传.");
    reset();
   } catch (IOException e) {
    e.printStackTrace();
    System.out.println("建立网络连接失败, 不能完成上传.");
    JOptionPane.showMessageDialog(null, "建立网络连接失败, 不能完成上传.", "系统提示", JOptionPane.ERROR_MESSAGE);
    dialog.setVisible(false);
    reset();
   }
  }

  public void run() {
   try {
    boolean resend = false;
    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
    writer.println(101);
    String returnValue = reader.readLine();
    if (returnValue != null && returnValue.equals(String.valueOf(201))) {
     // 验证用户登录
     writer.println("userName,password");
     returnValue = reader.readLine();
     if (returnValue != null && returnValue.equals(String.valueOf(201))) {

     } else {
      System.out.println("用户验证未通过.");

      return;
     }
     // 文件名称
     writer.println(fileCode + "," + realFile.getName());
     // 文件块信息
     writer.println(block);
     OutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
     RandomAccessFile srcFile = new RandomAccessFile(realFile, "r");
     byte[] buf = new byte[MAX_BUFFER_SIZE];
     long start = (block - 1) * MAX_FILE_BLOCK_SIZE;

     srcFile.seek(start);

     int times = 0;
     long total = getBlockSize(block);
     long read = 0;
     if (total % MAX_BUFFER_SIZE == 0) {
      times = (int) total / MAX_BUFFER_SIZE;
     } else {
      times = (int) total / MAX_BUFFER_SIZE + 1;
     }
     int i = 0;
     int length = -1;
     while ((length = srcFile.read(buf)) != -1 && i < times) {
      out.write(buf, 0, length);
      out.flush();
      i++;
      read += length;
     }
     socket.shutdownOutput();
     DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
     long receive = in.readLong();
     if (read != receive) {
      System.out.println("文件" + realFile.getName() + "第" + block + "块在上传过程中掉包(" + (read - receive) + "字节), 需重传.");
      resend = true;
     }
     reader.close();
     writer.close();
     srcFile.close();
     srcFile = null;
     out.close();
     socket.close();
    } else {
     // System.out.println("服务器未准备好.");
     System.out.print("服务器未准备好.");
     return;
    }
    if (resend) {
     new BlockSender(block);
    } else {
     synchronized (progressBar) {
      progressBar.setValue((progressBar.getValue() + (int) getBlockSize(block)));
     }
     if (blocks >= currentBlock) {
      new BlockSender(currentBlock);
      currentBlock += 1;
     } else {
      threads -= 1;
      if (threads == 0) {
       applet.finished();
       // Fix Me 这里建立备份文件夹的路径有问题?
       // java.io.File backupFolder = new java.io.File(realFile.getParentFile().getAbsolutePath() + java.io.File.separator + CommonUtils.formatDate(new Date(), "yyyyMMdd"));
       // if (logger.isDebugEnabled()) {
       // logger.debug("备份文件夹:" + backupFolder);
       // }
       // if (!backupFolder.exists()) {
       // backupFolder.mkdirs();
       // }
       //       
      }
     }
    }
   } catch (FileNotFoundException e) {
    // System.out.println("文件" + file.getFileCode() + "未能找到, 不能完成上传.");
    e.printStackTrace();
    reset();
   } catch (IOException e) {
    // System.out.println("发生文件或网络读写错误, 不能完成上传.");
    e.printStackTrace();
    reset();
   }
  }

 }

 private long getBlockSize(int block) {
  if (block == blocks && lastLength > 0) {
   return lastLength;
  }
  return MAX_FILE_BLOCK_SIZE;
 }

 /**
  * 恢复文件的状态
  */
 private void reset() {

 }
}

第二个:请求处理类, 处理文件上传和任务发送等业务

package test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.Socket;

//import org.apache.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;

//import com.excellence.exchange.domain.File;
//import com.excellence.exchange.domain.Missive;
//import com.excellence.exchange.domain.Organ;
//import com.excellence.exchange.domain.TransferTask;
//import com.excellence.exchange.facade.ExchangeFacade;
//import com.excellence.exchange.util.ConfigUtils;
//import com.excellence.exchange.util.ContextUtils;

/**
 * $Id: RequestHandler.java,v 1.1 2007/05/14 06:11:02 zhangyb Exp $
 *
 * 描述: 请求处理类, 处理文件上传和任务发送等业务
 * 
 * @author zhangyb
 * @version 1.0
 */
public class RequestHandler extends Thread {

 /** 文件交换命令--上传文件 */
 public static final int CMD_UPLOAD = 101;

 /** 文件交换命令--创建文件夹 */
 public static final int CMD_MAKE_DIR = 102;

 /** 文件交换命令--发送任务 */
 public static final int CMD_SEND_TASK = 103;

 /** 文件交换命令--机构同步 */
 public static final int CMD_SYNCHRONIZE_ORGAN = 104;

 /** 文件交换命令--机构注册 */
 public static final int CMD_REGISTER_ORGAN = 105;

 /** 请求返回状态值--就绪 */
 public static final int STATE_READY = 201;

 /** 请求返回状态值--错误 */
 public static final int STATE_ERROR = 202;

 /** 请求返回状态值--拒绝 */
 public static final int STATE_REJECT = 203;

 /** 请求返回状态值--完成 */
 public static final int STATE_FINISHED = 204;

 // private final Log logger = LogFactory.getLog(RequestHandler.class);

 private Socket request;

 public RequestHandler() {
 }

 public RequestHandler(Socket socket) {
  this.request = socket;
  start();
 }

 public void run() {
  BufferedReader reader = null;
  PrintWriter writer = null;
  DataOutputStream out = null;
  DataInputStream in = null;
  ObjectInputStream ois = null;
  ObjectOutputStream oos = null;
  try {
   reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
   writer = new PrintWriter(request.getOutputStream(), true);
   String command = reader.readLine();
   // 请求类型信息
   // if (logger.isDebugEnabled()) {
   System.out.println("请求类型代码:" + command);
   // }
   // 返回准备好响应
   writer.println(STATE_READY);
   writer.flush();
   // 接收用户信息(用户名,密码)
   String userInfo = reader.readLine();
   if (userInfo != null && !userInfo.equals("")) {
    // Fix me 如果要验证用户需实现下面方法
    String[] infos = userInfo.split(",");
    if (!login(infos[0], infos[1])) {
     System.out.println("无效的用户信息.");
     writer.println(STATE_REJECT);
     writer.flush();
     return;
    }
   } else {
    System.out.println("无效的用户信息.");
    writer.println(STATE_ERROR);
    writer.flush();
    return;
   }
   // 返回准备好响应
   writer.println(STATE_READY);
   writer.flush();
   // 接收文件名称(新文件名称,原始文件名称)
   String line = reader.readLine();
   String newName = "";
   if (line != null && !line.equals("")) {
    String[] names = line.split(",");
    newName = names[1];
   } else {
    System.out.println("无效的文件名称信息.");
    return;
   }
   // 接收文件块信息
   int block = 1;
   line = reader.readLine();
   if (line != null && !line.equals("")) {
    block = Integer.parseInt(line);
   } else {
    System.out.println("无效的文件块信息.");
    return;
   }
   in = new DataInputStream(new BufferedInputStream(request.getInputStream()));
   String root = "C:/";// "/tmp/youbin/";//ConfigUtils.getTargetFolder();
   // 如果是有父文件夹的情况需要重新处理???
   long receive = saveFile(new DataInputStream(new BufferedInputStream(request.getInputStream())), root + newName, block);
   out = new DataOutputStream(new BufferedOutputStream(request.getOutputStream()));
   out.writeLong(receive);
   out.flush();

  } catch (IOException e) {
   System.out.println("发生网络读写错误, 不能正常完成请求.");
   try {
    out.writeInt(RequestHandler.STATE_ERROR);
    out.flush();
   } catch (IOException e1) {
    System.out.println("网络读写错误, 未能返回错误到客户端.");
   }
  } finally {
   if (reader != null) {
    try {
     reader.close();
    } catch (IOException e) {
     System.out.println("非正常退出请求处理.");
    }
   }
   if (writer != null) {
    writer.close();
   }
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
     System.out.println("非正常退出请求处理.");
    }
   }
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
     System.out.println("非正常退出请求处理.");
    }
   }
   if (ois != null) {
    try {
     ois.close();
    } catch (IOException e) {
     System.out.println("非正常退出请求处理.");
    }
   }
   if (oos != null) {
    try {
     oos.close();
    } catch (IOException e) {
     System.out.println("非正常退出请求处理.");
    }
   }
   if (request != null) {
    try {
     request.close();
    } catch (IOException e) {
     System.out.println("非正常退出请求处理.");
    }
   }
  }
 }

 /**
  * 保存文件到指定的文件夹中
  *
  * @param in 包含文件数据的流
  * @param fileName 在文件夹中的文件名称
  * @param block 当前文件块的序号
  * @return 返回实际收到的长度返回给客户端验证完整性
  */
 private long saveFile(InputStream in, String fileName, int block) {
  long receive = 0;
  try {
   RandomAccessFile targetFile = new RandomAccessFile(fileName, "rws");
   targetFile.seek((block - 1) * UploadHandler.MAX_FILE_BLOCK_SIZE);
   byte[] buf = new byte[UploadHandler.MAX_BUFFER_SIZE];
   int length = 0;
   while ((length = in.read(buf)) != -1) {
    targetFile.write(buf, 0, length);
    receive += length;
   }
   targetFile.close();
   targetFile = null;
  } catch (FileNotFoundException e) {
   e.printStackTrace();
   // 这里不应该出现异常.
  } catch (IOException e) {
   // Fix Me 这里需要处理
   e.printStackTrace();
  }
  return receive;
 }

 /**
  * 验证用户有效性
  *
  * TODO 如果需要验证则实现下面方法
  *
  * @param userName
  * @param password
  * @return
  */
 private boolean login(String userName, String password) {
  return true;
 }

}

 

分享到:
评论
4 楼 classicbride 2007-08-06  
能不能分享一下源码呀..我也在项目中遇到这个问题..超大文件上传..头正大着呢...看到你的思路不错...你那样速度方面呢...服务器性能有什么要求...谢谢
3 楼 youbin_ 2007-05-25  
已经搞定
2 楼 shaucle 2007-05-23  
怎么像csdn?
1 楼 youbin_ 2007-05-23  
如果大家有其它实现方法也可以在这里提提,本人感激不尽。

相关推荐

    C#.NET案例开发集锦代码7-11章

    第七章 案例1通过委托实现异步调用 案例2使用定时器执行方法 案例3...多线程TCP服务端实现 案例6用Socket连接获取客户端地址 案例8如何实现UDP通讯协议 案例9如何使用STMP协议发送电子邮件 第九章...

    老男孩python 四期

    ※用多线程的方式并发连接登录多台LINUX服务器,实现并发命令操作、文件分发,实时结果返回,并可根据服务器性能控制线程量,千台机器改密码1分钟完成。 3、HTML/JAVAScript基础 4、WEB JS Package BootStrap 介绍...

    JavaEE求职简历-姓名-JAVA开发工程师-范文.doc

    熟练掌握java基础,包括面向对象、多线程、IO流、UDP/TCP、socket、文件上传下载、XML解析、JSON解析、servlet、filter、listener、HTTP协议等等; 熟练掌握 使用jdbc标准连接数据库,掌握DDL、DML、DCL、DQL和...

    传智播客的android开发源代码

    28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice.avi 所在项目:mobileAddressQuery & Web端应用:web 30_多线程下载原理.avi 所在项目:net 31_...

    source.zip

    28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice.avi 所在项目:mobileAddressQuery & Web端应用:web 30_多线程下载原理.avi 所在项目:net 31_...

    8天快速掌握Android教程源码

    28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice.avi 所在项目:mobileAddressQuery & Web端应用:web 30_多线程下载原理.avi 所在项目:net 31_...

    JAVA上百实例源码以及开源项目

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    java jdk实列宝典 光盘源代码

    一个支持多线程的服务器框架,GeneralServer.java; 代理服务器,ProxyServer.java; telnet客户端,访问系统的telnet服务实质上是与telnet服务建立socket连接,默认的telnet服务的端口是23,TelnetClient.java; ...

    JAVA 范例大全 光盘 资源

    实例133 碰撞的球(多线程) 382 实例134 钟表(多线程) 387 实例135 模拟生产者与消费者 392 实例136 仿迅雷下载文件 396 第15章 图形编程 403 实例137 多变的按钮 403 实例138 自制对话框 405 实例139 ...

    最新Python3.5零基础+高级+完整项目(28周全)培训视频学习资料

    Django获取多个数据以及文件上传 Django的CBV和FBV Django模板语言循环字典 Django基于正则表达式的URL Django对应的路由名称 Django路由分发 DjangoORM基本创建基本类型以及生成数据库结构 DjangoORM使用mysql注意 ...

    Java源码 SpringMVC Mybatis Shiro Bootstrap Rest Webservice

    4. 文件上传、多线程下载服务化、发送邮件、短信服务化、部门信息服务化、产品信息服务化、信息发布服务化、我的订阅服务化、我的任务服务化、公共链接、我的收藏服务化等 系统模块: 1. 用户管理: 用户信息...

    java源码包---java 源码 大量 实例

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    JAVA上百实例源码以及开源项目源代码

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    java源码包2

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    java源码包3

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    java源码包4

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    python入门到高级全栈工程师培训 第3期 附课件代码

    01 selctors实现文件上传与下载 02 html的介绍 03 html文档树的概念 04 meta标签以及一些基本标签 05 img标签和列表标签 06 form表单之input标签 07 通过form向server端发送数据 08 form表单之select标签 09 table...

Global site tag (gtag.js) - Google Analytics