`
youbin_
  • 浏览: 6066 次
  • 性别: 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  
如果大家有其它实现方法也可以在这里提提,本人感激不尽。

相关推荐

    java多线程+Socket实现的漂亮QQ

    ### Java多线程+Socket实现的漂亮QQ #### 技术要点分析 ##### 1. Java Swing Java Swing 是一个用于构建图形用户界面 (GUI) 的轻量级组件集,它为开发人员提供了丰富的功能来设计复杂的用户界面。在本项目中,...

    安卓使用socket上传文件demo

    在这个“安卓使用socket上传文件demo”中,我们将深入探讨如何利用Socket在安卓应用中实现文件上传功能。 首先,让我们了解Socket的基本概念。Socket是网络通信的基础,它允许两个网络端点(通常是服务器和客户端)...

    socket-支持断点续传java多线程下载

    ### socket支持断点续传Java多线程下载技术解析 #### 概述 在软件开发领域,特别是对于大型文件的下载需求,断点续传功能显得尤为重要。它能够确保在网络不稳定的情况下,用户仍然可以顺利地完成文件下载。本文将...

    ASP.Net 多线程-IIS搭建-线程池-video

    通过这个视频教程,开发者将能够掌握ASP.NET中多线程的应用,理解如何在IIS环境下优化Web应用,同时学习到Socket编程、模拟IIS、一般处理程序的使用,以及如何实现异步无刷新分页和有效利用服务器端控件。...

    文件的上传与下载

    多线程是并发执行多个任务的技术,尤其在处理大文件上传下载时,多线程可以显著提高程序性能。例如,当上传一个大文件时,可以创建两个线程,一个负责读取本地文件,另一个负责发送数据到服务器。这样,读取和发送...

    C++实现http/https/ftp文件下载

    7. **FTP协议处理**:FTP协议有多种命令,如USER/PASS用于认证,STOR/RETR用于文件上传和下载,需要正确实现FTP命令交互。 8. **文件I/O操作**:下载的数据需要写入本地文件,涉及文件打开、读写、关闭等操作,注意...

    Windows平台下多线程下载工具的研究与设计

    此外,我已开发了一款下载器,它运用多线程技术来提升下载速度,支持HTTP和FTP协议,并具有文件分块传输、文件重命名、速度控制和复制URL到剪贴板等更多功能。该下载器已在Windows平台上进行了测试,运行稳定且速度...

    java+mysql文件上传器

    在文件上传场景下,客户端(通常是Web应用程序)通过Socket连接到服务器,然后将文件的二进制数据流式传输到服务器端。 1. 创建Socket连接:客户端使用Socket类的构造函数指定服务器的IP地址和端口号,建立连接。 2...

    多线程断点续传下载工具(支持HTTP&FTP)源代码

    多线程下载是将一个文件分成多个部分,每个部分由单独的线程进行下载。这种方式可以充分利用网络带宽,提高下载速度。在VC++6.0环境下,开发者通常会利用MFC(Microsoft Foundation Classes)库来构建Windows应用...

    c#文件上传案例

    同时,服务端可能需要优化存储策略,例如使用多线程处理文件,或者使用流式存储来减少内存消耗。 综上所述,本案例提供的C#文件上传方案涵盖了从基础的C#编程、Socket通信到文件上传的完整流程,对于理解C#文件操作...

    商业编程-源码-FTP、HTTP 多线程断点续传下载文件.zip

    多线程下载是提高文件下载速度的一种技术,通过同时启动多个下载线程,可以从服务器并行获取文件的不同部分,从而加快整体下载速度。在实现中,通常需要对文件进行分割,每个线程负责下载一部分,然后在本地进行合并...

    C#文件上传下载

    在IT行业中,文件上传下载是网络应用的基本功能之一,尤其在Web开发中至关重要。本教程主要聚焦于使用C#语言实现TCP协议下的文件上传下载功能。C#是一种面向对象的编程语言,它提供了丰富的库和工具来处理网络通信,...

    文件上传的实例

    在IT领域,文件上传是一项常见的功能,特别是在web应用和云服务中。文件上传允许用户将本地计算机上的数据或文件传输到服务器,以便于共享、存储或处理。本篇将深入探讨文件上传的相关知识点,包括其原理、实现方式...

    上传下载文件

    本文将详细探讨如何实现“上传下载文件”功能,以及它与socket通讯和多线程技术的关系。 首先,让我们了解上传和下载的基本概念。上传是指将本地计算机上的文件发送到远程服务器的过程,而下载则是相反的操作,即从...

    delphi socket编程教程

    - **FTP服务器**:基于TCP Socket实现文件上传下载。 - **聊天室**:使用UDP Socket实现实时的消息传递。 - **Web服务器**:HTTP协议是基于TCP的,可以使用Socket实现简单的HTTP服务器。 通过学习Delphi Socket...

    Java实现简单的web文件服务器

    通过这个项目,你可以学习到Java网络编程的基本原理,了解TCP和UDP的区别,掌握多线程的应用,并熟悉文件操作。实践中遇到的问题和解决过程将加深你对这些概念的理解。如果你是初学者,这是一个很好的起点,可以帮你...

    java socket 学习资料

    - 文件上传涉及到处理multipart/form-data格式的POST请求。 5. **POST请求的常见问题**: - "POST No Response"问题通常是由于服务器没有正确处理POST请求,或者客户端没有正确关闭Socket导致的。 - 常见解决...

    上传下载文件—C写的源码

    8. **性能优化**:为了提高上传和下载速度,可以使用多线程或多进程,同时传输多个数据块。此外,还可以考虑使用非阻塞I/O或异步I/O来改善性能。 9. **安全性**:在实际应用中,文件上传和下载需要考虑安全性。这...

    delphi 跨平台 socket

    在 Delphi 中,可以使用 Indy 或其他库来创建 HTTP 客户端和服务器,处理 GET、POST 等请求,实现文件上传下载、Web服务调用等功能。 3. **epoll (Linux)**:epoll 是 Linux 系统提供的高效 I/O 事件通知机制,适用...

    SocketMgr_downcc_socket_

    3. 并发控制:支持多线程或异步IO,提高文件下载速度。 4. 断点续传:记录已下载的部分,当连接中断后可以从上次的位置继续下载。 5. 安全性:可能集成SSL/TLS支持,提供安全的数据传输。 总结来说,SocketMgr_...

Global site tag (gtag.js) - Google Analytics