2019独角兽企业重金招聘Python工程师标准>>>

最近公司培训,所以收集整理了一些关于上传下载的资料,进行了整理与大家分享。

Struts对文件上传的支持非常好,它是通过jakarta comons filelupload库的无缝集成而做到这一点的。(此次主要是基于struts1的上传下载).

特别提示:要想使用html表单上传一个或多个文件,须把表单的enctype属性设置为multipart/form-data,把它的method属性设置为post.

借助Apache项目下提供的commons-net-1.4.1.jar来实现对FTP的上传下载的操作,实现对服务器上文件的操作。

在实现ftp上传下载前我们需要在本地搭建一个ftp的环境,本例采用的是Easy  FTP Server构建ftp环境
    我们还需要借助Apache项目下提供的commons-net-1.4.1.jar来实现对FTP的上传下载的操作。
应用:
      我们可以通过ftpd的上传下载实现文件跨服务器的迁移,应用自己的程序
对服务器进行上传,下载,修改,更新等操作。

我们平常下载时有可能会出现断网被迫中止,此时往往我们希望下载不是从头开始的,我们就需要要到断点续传的功能,节约时间,提高客户的体验度。

断点续传的原理
其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已。 
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 
假设服务器域名为 wwww.sjtu.edu.cn,文件名为 down.zip。 
GET /down.zip HTTP/1.1 
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms- 
excel, application/msword, application/vnd.ms-powerpoint, */* 
Accept-Language: zh-cn 
Accept-Encoding: gzip, deflate 
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) 
Connection: Keep-Alive
服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:
200 
Content-Length=106786028 
Accept-Ranges=bytes 
Date=Mon, 30 Apr 2001 12:56:11 GMT 
ETag=W/"02ca57e173c11:95b" 
Content-Type=application/octet-stream 
Server=Microsoft-IIS/5.0 
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT

所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给 Web 服务器的时候要多加一条信息 -- 从哪里开始。 
下面是用自己编的一个"浏览器"来传递请求信息给 Web 服务器,要求从 2000070 字节开始。 
GET /down.zip HTTP/1.0 
User-Agent: NetFox 
RANGE: bytes=2000070- 
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔细看一下就会发现多了一行 RANGE: bytes=2000070- 
这一行的意思就是告诉服务器 down.zip 这个文件从 2000070 字节开始传,前面的字节不用传了。 
服务器收到这个请求以后,返回的信息如下: 
206 
Content-Length=106786028 
Content-Range=bytes 2000070-106786027/106786028 
Date=Mon, 30 Apr 2001 12:55:20 GMT 
ETag=W/"02ca57e173c11:95b" 
Content-Type=application/octet-stream 
Server=Microsoft-IIS/5.0 
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
和前面服务器返回的信息比较一下,就会发现增加了一行: 
Content-Range=bytes 2000070-106786027/106786028 
返回的代码也改为 206 了,而不再是 200 了。

package common.http;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 一个多线程支持断点续传的工具类<br/>
 */
public class HttpDownloader {

private final Log log = LogFactory.getLog(getClass().getName());
private int threads = 5; // 总共的线程数
private int maxThreads = 10; // 最大的线程数
private String destUrl; // 目标的URL
private String savePath; // 保存的路径
private File lockFile;// 用来保存进度的文件
private String userAgent = "jHttpDownload";
private boolean useProxy = false;
private String proxyServer;
private int proxyPort;
private String proxyUser;
private String proxyPassword;
private int blockSize = 1024 * 4; // 4K 一个块
// 1个位代表一个块,,用来标记是否下载完成
private byte[] blockSet;
private int blockPage; // 每个线程负责的大小
private int blocks;
private boolean running; // 是否运行中,避免线程不能释放
// =======下载进度信息
private long beginTime;
private AtomicLong downloaded = new AtomicLong(0); // 已下载的字节数\
private long fileLength; // 总的字节数
// 监控线程,用来保存进度和汇报进度
private MonitorThread monitorThread = new MonitorThread();

public HttpDownloader(String destUrl, String savePath, int threads) {
this.threads = threads;
this.destUrl = destUrl;
this.savePath = savePath;
}

public HttpDownloader(String destUrl, String savePath) {
this(destUrl, savePath, 5);
}

/**
* 开始下载
*/
public boolean download() {
log.info("下载文件" + destUrl + ",保存路径=" + savePath);
beginTime = System.currentTimeMillis();//设置开始时间用于记录下载所用的时间
boolean ok = false;
try {
File saveFile = new File(savePath);
lockFile = new File(savePath + ".lck");
if (lockFile.exists() && !lockFile.canWrite()) {
throw new Exception("文件被锁住,或许已经在下载中了");
}
File parent = saveFile.getParentFile();
if (!parent.exists()) {
log.info("创建目录=" + parent.getAbsolutePath());
}
if (!parent.canWrite()) {
throw new Exception("保存目录不可写");
}
if (saveFile.exists()) {
if (!saveFile.canWrite()) {
throw new Exception("保存文件不可写,无法继续下载");
}
log.info("检查之前下载的文件");
if (lockFile.exists()) {
log.info("加载之前下载进度");
loadPrevious();
}
} else {
lockFile.createNewFile();
}
// 1初始化httpClient
HttpURLConnection httpUrlConnection = getHttpConnection(0);
String contentLength = httpUrlConnection
.getHeaderField("Content-Length");
if (contentLength != null) {
try {
fileLength = Long.parseLong(contentLength);
} catch (Exception e) {
}
}
log.info("下载文件的大小:" + fileLength);
if (fileLength <= 0) {
// 不支持多线程下载,采用单线程下载
log.info("服务器不能返回文件大小,采用单线程下载");
threads = 1;
}
if (httpUrlConnection.getHeaderField("Content-Range") == null) {
log.info("服务器不支持断点续传");
threads = 1;
} else {
log.info("服务器支持断点续传");
}
//
if (fileLength > 0 && parent.getFreeSpace() < fileLength) {
throw new Exception("磁盘空间不够");
}
if (fileLength > 0) {
int i = (int) (fileLength / blockSize);
if (fileLength % blockSize > 0) {
i++;
}
blocks = i;
} else {
// 一个块  
blocks = 1;
}
if (blockSet != null) {
log.info("检查文件,是否能够续传");
if (blockSet.length != fileLength / (8l * blockSize)) {
log.info("文件大小已改变,需要重新下载");
blockSet = null;
}
}
if (blockSet == null) {
blockSet = BitUtil.createBit(blocks);
}
log.info("文件的块数:" + blocks + "," + blockSet.length);
if (threads > maxThreads) {
log.info("超过最大线程数,使用最大线程数");
threads = maxThreads;
}
blockPage = blocks / threads; // 每个线程负责的块数
log.info("分配线程。线程数量=" + threads + ",块总数=" + blocks + ",总字节数="
+ fileLength + ",每块大小=" + blockSize + ",块/线程=" + blockPage);
// 检查
running = true;
// 创建一个线程组,方便观察和调试
ThreadGroup downloadGroup = new ThreadGroup("download");
for (int i = 0; i < threads; i++) {
int begin = i * blockPage;
int end = (i + 1) * blockPage;
if (i == threads - 1 && blocks % threads > 0) {
// 如果最后一个线程,有余数,需要修正
end = blocks;
}
// 扫描每个线程的块是否有需要下载的
boolean needDownload = false;
for (int j = begin; j < end; j++) {
if (!BitUtil.getBit(blockSet, j)) {
needDownload = true;
break;
}
}
if (!needDownload) {
log.info("所有块已经下载完毕.Begin=" + begin + ",End=" + end);
}
// 启动下载其他线程
DownloadThread downloadThread = new DownloadThread(
downloadGroup, i, begin, end);
downloadThread.start();
}
monitorThread.setStop(false);
monitorThread.start();
// 也可以用Thread.join 实现等待进程完成
while (downloadGroup.activeCount() > 0) {
Thread.sleep(2000);
}
ok = true;
} catch (Exception e) {
e.printStackTrace();
log.error(e);
} finally {
// closeHttpClient();
}
if (ok) {
log.info("删除进度文件:" + lockFile.getAbsolutePath());
lockFile.delete();
}
monitorThread.setStop(true);
log.info("下载完成,耗时:"
+ getTime((System.currentTimeMillis() - beginTime) / 1000));
return ok;
}

private void loadPrevious() throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
FileInputStream inStream = new FileInputStream(lockFile);
byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = inStream.read(buffer))) {
outStream.write(buffer, 0, n);
}
outStream.close();
inStream.close();
blockSet = outStream.toByteArray();
log.info("之前的文件大小应该是:" + blockSet.length * 8l * blockSize + ",一共有:"
+ blockSet.length + "块");
}

private HttpURLConnection httpConnection0; // 用来快速返回,减少一次连接

private HttpURLConnection getHttpConnection(long pos) throws IOException {
if (pos == 0 && httpConnection0 != null) {
return httpConnection0;
}
URL url = new URL(destUrl);
log.debug("开始一个Http请求连接。Url=" + url + "定位:" + pos + "\n");
// 默认的会处理302请求
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection httpConnection = null;
if (useProxy) {
log.debug("使用代理进行连接.ProxyServer=" + proxyServer + ",ProxyPort="
+ proxyPort);
SocketAddress addr = new InetSocketAddress(proxyServer, proxyPort);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
httpConnection = (HttpURLConnection) url.openConnection(proxy);
if (proxyUser != null && proxyPassword != null) {
String encoded = new String(Base64.encodeBase64(new String(
proxyUser + ":" + proxyPassword).getBytes()));
httpConnection.setRequestProperty("Proxy-Authorization",
"Basic " + encoded);
}
} else {
httpConnection = (HttpURLConnection) url.openConnection();
}
httpConnection.setRequestProperty("User-Agent", userAgent);
httpConnection.setRequestProperty("RANGE", "bytes=" + pos + "-");
int responseCode = httpConnection.getResponseCode();
log.debug("服务器返回:" + responseCode);
Map<String, List<String>> headers = httpConnection.getHeaderFields();
Iterator<String> iterator = headers.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = "";
for (String v : headers.get(key)) {
value = ";" + v;
}
log.debug(key + "=" + value);
}
if (responseCode < 200 || responseCode >= 400) {
throw new IOException("服务器返回无效信息:" + responseCode);
}
if (pos == 0) {
httpConnection0 = httpConnection;
}
return httpConnection;
}

public void returnConnection(HttpURLConnection connecton) {
if (connecton != null)
connecton.disconnect();
connecton = null;
}

private String getDesc() {
long downloadBytes = downloaded.longValue();

return String
.format(
"已下载/总大小=%s/%s(%s),速度:%s,耗时:%s,剩余大小:%d",
getFileSize(downloadBytes),
getFileSize(fileLength),
getProgress(fileLength, downloadBytes),
getFileSize(downloadBytes
/ ((System.currentTimeMillis() - beginTime) / 1000 + 1)),
getTime((System.currentTimeMillis() - beginTime) / 1000),
fileLength - downloadBytes);
}

private String getFileSize(long totals) {
// 计算文件大小
int i = 0;
String j = "BKMGT";
float s = totals;
while (s > 1024) {
s /= 1024;
i++;
}
return String.format("%.2f", s) + j.charAt(i);
}

private String getProgress(long totals, long read) {
if (totals == 0)
return "0%";
return String.format("%d", read * 100 / totals) + "%";
}

private String getTime(long seconds) {
int i = 0;
String j = "秒分时天";
long s = seconds;
String result = "";
while (s > 0) {
if (s % 60 > 0) {
result = String.valueOf(s % 60) + (char) j.charAt(i) + result;
}
s /= 60;
i++;
}
return result;
}

/**
* 一个下载线程.
*/
private class DownloadThread extends Thread {

private RandomAccessFile destFile; // 用来实现保存的随机文件
private int id = 0;
private int blockBegin = 0; // 开始块
private int blockEnd = 0; // 结束块
private long pos;// 绝对指针

private String getThreadName() {
return "DownloadThread-" + id + "=>";
}

public DownloadThread(ThreadGroup group, int id, int blockBegin,
int blockEnd) throws Exception {
super(group, "downloadThread-" + id);
this.id = id;
this.blockBegin = blockBegin;
this.blockEnd = blockEnd;
this.pos = 1l * blockBegin * blockSize; // 转换为长整型
destFile = new RandomAccessFile(savePath, "rw");
}

public void run() {
BufferedInputStream inputStream = null;
try {
log.info(getThreadName() + "下载线程." + this.toString());
log.info(getThreadName() + ":定位文件位置.Pos=" + 1l * blockBegin
* blockSize);
destFile.seek(1l * blockBegin * blockSize);
log.info(getThreadName() + ":开始下载.[ " + blockBegin + " - "
+ blockEnd + "]");

HttpURLConnection httpConnection = getHttpConnection(pos);
inputStream = new BufferedInputStream(httpConnection
.getInputStream());
byte[] b = new byte[blockSize];
while (blockBegin < blockEnd) {
if (!running) {
log.info(getThreadName() + ":停止下载.当前块:" + blockBegin);
return;
}
log.debug(getThreadName() + "下载块=" + blockBegin);
int counts = 0; // 已下载字节数
if (BitUtil.getBit(blockSet, blockBegin)) {
log.debug(getThreadName() + ":块下载已经完成=" + blockBegin);
destFile.skipBytes(blockSize);
int skips = 0;
while (skips < blockSize) {
skips += inputStream.skip(blockSize - skips);
}
downloaded.addAndGet(blockSize);

} else {
while (counts < blockSize) {
int read = inputStream.read(b, 0, blockSize
- counts);
if (read < 0)
break;
counts += read;
destFile.write(b, 0, read);
downloaded.addAndGet(read);
}
BitUtil.setBit(blockSet, blockBegin, true); // 标记已经下载完成
}
blockBegin++;
}
httpConnection.disconnect();
log.info(getThreadName() + "下载完成.");
return;
} catch (Exception e) {
log.error(getThreadName() + "下载错误:" + e.getMessage());
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
} catch (Exception te) {
log.error(te);
}
try {
if (destFile != null)
destFile.close();
} catch (Exception te) {
log.error(te);
}
}
}
}

// 监控线程,并保存进度,方便下次断点续传
private class MonitorThread extends Thread {
boolean stop = false;

public void setStop(boolean stop) {
this.stop = stop;
}

public void run() {
FileOutputStream saveStream = null;
try {
while (running && !stop) {
saveStream = new FileOutputStream(lockFile);
log.info(getDesc());
// 保存进度
saveStream.write(blockSet, 0, blockSet.length);
sleep(1000);
saveStream.close();
}
} catch (Exception e) {
e.printStackTrace();
log.error(e);
} finally {

}
}
}

// 用来操作位的工具
private static class BitUtil {
public static byte[] createBit(int len) {
int size = len / Byte.SIZE;
if (len % Byte.SIZE > 0) {
size++;
}
return new byte[size];
}

/** 取出某位,是0 还是1 */
public static boolean getBit(byte[] bits, int pos) {
int i = pos / Byte.SIZE;
int b = bits[i];
int j = pos % Byte.SIZE;
byte c = (byte) (0x80 >>> (j - 1));
return b == c;
}

/** 设置某位,是0 还是1 */
public static void setBit(byte[] bits, int pos, boolean flag) {
int i = pos / Byte.SIZE;
byte b = bits[i];
int j = pos % Byte.SIZE;
byte c = (byte) (0x80 >>> (j - 1));
if (flag) {
bits[i] = (byte) (b | c);
} else {
c = (byte) (0xFF ^ c);
bits[i] = (byte) (b & c);
}
}
}
}

转载于:https://my.oschina.net/songhongxu/blog/156353

文件的上传下载(一)相关推荐

  1. Java使用SFTP和FTP两种连接服务器的方式实现对文件的上传下载

    一.Java实现对SFTP服务器的文件的上传下载: 1.添加maven依赖: <dependency><groupId>com.jcraft</groupId>&l ...

  2. 文件的上传下载功能的实现(包括进度条)[telerik控件]

    文件的上传下载功能的实现(包括进度条) 1.准备工作 首先我们需要Telerik控件,数据库,上传文件文件夹. Telerik控件: RadUpload.RadProgressManager.RadP ...

  3. ssm框架验证码图片加载不出_基于SSM框架的文件图片上传/下载功能实现

    前一段时间很多做毕业设计的同学问:如何写图片和文件的上传下载功能,今天正好有时间,所以就做了一个案例,详细的讲解这个功能. 框架结构: 对于很多做过开发的而言,上传功能肯定都用过,而且用到的场景很多, ...

  4. ACTIVEX实现大文件FTP上传下载---上

    ACTIVEX实现大文件FTP上传 在Windows 操作系统下,有一个重要的机制,就是OLE ,就是可以让某个应用程序(OLE Controller)访问其它应用程序(OLE Server)所提供的 ...

  5. 基于layui.upload.js 拖拽文件/文件夹上传下载

    layui.upload.js 拖拽文件/文件夹上传下载 前言 js代码 页面使用(我这里用的是uploader.jsp) CSS文件 上传效果 总结 前言 项目需求完成文件上传,可以拖拽上传文件/文 ...

  6. 使用JSP+Servlet实现文件的上传下载上传

    <!DOCTYPE html > <html> <head> <meta charset="UTF-8"> <title> ...

  7. 云计算学习笔记004---hadoop的简介,以及安装,用命令实现对hdfs系统进行文件的上传下载

    1.Hadoop简介 1.hadoop的诞生 l  Nutch和Lucene之父Doug Cutting在2006年完成Hadoop项目. l  Hadoop并不是一个单词,它来源于DougCutti ...

  8. java 文件下载 组件_java文件夹上传下载组件

    核心原理: 该项目核心就是文件分块上传.前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题. *如何分片: *如何合成一个文件: *中断了从哪个分片开始. ...

  9. FTP客户端--实现FTP文件的上传下载功能

    现在是2017.6.16的1点多,这几天刚好做了个FTP客户端的计网实验,就把思路过程和源码发上来吧! 一.设计思路:首先,登陆指定的FTP服务器(指定服务器的IP和用户名,密码,端口号若无就默认为2 ...

最新文章

  1. 是否会回到起点.回忆只能是回忆
  2. m4a录音文件损坏修复_电脑录音软件哪个好?分享这款录音软件,供你参考!
  3. 淘汰过时的工具也有错?微软的 Blazor 框架会是下一个 SilverLight?
  4. 如何在Java中生成比特币钱包地址
  5. shell脚本实现文件锁功能
  6. Redis环境配置和命令语句
  7. Android 四大组件学习之BroadcastReceiver三
  8. iOS学习之单例模式
  9. shotcut视频压缩
  10. 普中科技51单片机开发板
  11. 印度网民集体删除中国APP,网友评论亮了
  12. 教你如何使用 python 制作一个简单的密码本
  13. 美团后台开发秋招面经汇总(更新至2021-08-13)
  14. 阿里云服务器绑定域名、esc绑定域名、域名备案
  15. Vue3+TypeScript从入门到精通系列之:Try changing the lib compiler option to es2015 or later
  16. 智能合约(Smart contract)
  17. (你也可以像别人一样对框架底层源码来去自如)23种设计模式之外观模式
  18. 输出n以内的所有质数到文本文件
  19. Java修炼之凡界篇 筑基期 第02卷 语法 第04话 类型转换
  20. 数组:翻转指定的次数

热门文章

  1. java手游 《剑心》_java
  2. c语言程序设计 第三版 哈工大,c语言程序设计 哈工大 苏小红 第三章习题
  3. tomcat 设置java_为tomcat配置java环境变量
  4. python体育竞技分析代码200行_使用Python进行体育竞技分析(预测球队成绩)
  5. windows dc linux,active-directory – Linux AD集成,使用Windows Server 2012 DC时无法登录
  6. java 折线图 放大 缩小_可拖拉放大缩小HC折线图 | JShare
  7. 初步认识java中的方法
  8. Xtrabackup安装以及应用
  9. 产品经理与产品运营哪个更有前途/钱途?
  10. 中国行业趋势报告——2022年度特别报告