多线程下载图片,输出流用的是BufferedOutputStream!
如果线程的启动顺序是1、2、3、4、5....
那么下载的图片是没有问题,可以正常打开。
当然线程的启动顺序我们是无法控制的
顺序一乱,那么下载的图片就会有问题!
无法预览,甚至只有某一部份是正常的
请问,这种问题怎样解决...?以下是多线程下载图片的代码:
public class DemoThread extends Thread {
private DownloadTest dts = null;
private int id = 0;
private long startPos = 0;
private long endPos = 0;
private CountDownLatch latch = null;
private BufferedOutputStream output = null;
/**
*
* @param dts DownloadTest的引用
* @param latch CountDownLatch对象
* @param id 线程ID
* @param startPos 下载的开始位置
* @param endPos 下载的结束位置
* @param output 输出流对象
* @throws FileNotFoundException
*/
public DemoThread(DownloadTest dts, CountDownLatch latch, int id, long startPos, long endPos, BufferedOutputStream output){
this.dts = dts;
this.latch = latch;
this.id = id;
this.startPos = startPos;
this.endPos = endPos;
this.output = output;
}
@Override
public void run() {
System.out.println("线程 " + id + " 启动...");
HttpURLConnection httpConn = null;
InputStream input = null;
BufferedOutputStream output1 = null;
long count = 0;
long threadDownloadLength = endPos - startPos;
try{ httpConn = (HttpURLConnection) dts.getUrl().openConnection();
// 设置连接超时时间为10000ms
httpConn.setConnectTimeout(10000);
// 设置读取数据超时时间为10000ms
httpConn.setReadTimeout(10000);
if(startPos < endPos){
httpConn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
httpConn.setRequestProperty("Accept","image/gif,image/x-xbitmap,application/msword,*/*");
System.out.println("线程 "+ id + " 从 " + startPos + " =====> " + endPos);
//判断http status是否为 206 Partial Content或者200 OK
if(httpConn.getResponseCode() == HttpURLConnection.HTTP_OK ||
httpConn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
input = httpConn.getInputStream();
int byteread = 0;
byte[] b = new byte[1024]; while((byteread = input.read(b)) > 0){
output.write(b, 0, byteread); count += byteread;
} if(count >= threadDownloadLength){
System.out.println(id+" OK.");
}
output1.flush();
output.flush();
} else {
System.out.println("线程 "+ id + ": 状态码=" + httpConn.getResponseCode()
+ ", 错误消息=" + httpConn.getResponseMessage());
}
}
System.out.println("线程 " + id + " 下载完成!");
// 子线程下载完指定的数据后将计数器减1
latch.countDown();
} catch (java.net.SocketException se){
se.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != input) input.close();
if(null != output1) output1.close();
} catch (IOException e) {
e.printStackTrace();
}
if(null != httpConn) httpConn.disconnect();
}
}
}
如果线程的启动顺序是1、2、3、4、5....
那么下载的图片是没有问题,可以正常打开。
当然线程的启动顺序我们是无法控制的
顺序一乱,那么下载的图片就会有问题!
无法预览,甚至只有某一部份是正常的
请问,这种问题怎样解决...?以下是多线程下载图片的代码:
public class DemoThread extends Thread {
private DownloadTest dts = null;
private int id = 0;
private long startPos = 0;
private long endPos = 0;
private CountDownLatch latch = null;
private BufferedOutputStream output = null;
/**
*
* @param dts DownloadTest的引用
* @param latch CountDownLatch对象
* @param id 线程ID
* @param startPos 下载的开始位置
* @param endPos 下载的结束位置
* @param output 输出流对象
* @throws FileNotFoundException
*/
public DemoThread(DownloadTest dts, CountDownLatch latch, int id, long startPos, long endPos, BufferedOutputStream output){
this.dts = dts;
this.latch = latch;
this.id = id;
this.startPos = startPos;
this.endPos = endPos;
this.output = output;
}
@Override
public void run() {
System.out.println("线程 " + id + " 启动...");
HttpURLConnection httpConn = null;
InputStream input = null;
BufferedOutputStream output1 = null;
long count = 0;
long threadDownloadLength = endPos - startPos;
try{ httpConn = (HttpURLConnection) dts.getUrl().openConnection();
// 设置连接超时时间为10000ms
httpConn.setConnectTimeout(10000);
// 设置读取数据超时时间为10000ms
httpConn.setReadTimeout(10000);
if(startPos < endPos){
httpConn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
httpConn.setRequestProperty("Accept","image/gif,image/x-xbitmap,application/msword,*/*");
System.out.println("线程 "+ id + " 从 " + startPos + " =====> " + endPos);
//判断http status是否为 206 Partial Content或者200 OK
if(httpConn.getResponseCode() == HttpURLConnection.HTTP_OK ||
httpConn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
input = httpConn.getInputStream();
int byteread = 0;
byte[] b = new byte[1024]; while((byteread = input.read(b)) > 0){
output.write(b, 0, byteread); count += byteread;
} if(count >= threadDownloadLength){
System.out.println(id+" OK.");
}
output1.flush();
output.flush();
} else {
System.out.println("线程 "+ id + ": 状态码=" + httpConn.getResponseCode()
+ ", 错误消息=" + httpConn.getResponseMessage());
}
}
System.out.println("线程 " + id + " 下载完成!");
// 子线程下载完指定的数据后将计数器减1
latch.countDown();
} catch (java.net.SocketException se){
se.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != input) input.close();
if(null != output1) output1.close();
} catch (IOException e) {
e.printStackTrace();
}
if(null != httpConn) httpConn.disconnect();
}
}
}
解决方案 »
- 有关japplet问题
- java如何导出txt文件
- weblogic 好像根本不编译我写的代码一样
- 持久化我使用HIBERNATE,如何判断在运行的时候,底层使用的数据库?
- 用j2ee写的工资管理系统
- 添加,删除,修改等按钮当我点击按钮,希望有相应的反应,
- 江湖告急,jsp生成图片的问题(由于前贴的给分太少了,重发)
- 安装j2ee之后怎么样配置classpath
- 有没有任何办法(有没有什么环境),进行JAVASCRIPT的单步调试
- 急急!请问在Applet中如何用连接和按钮打开一个Servlet页面(最好能提交数据)
- 新建了个struts1.3的web项目,在项目中的一个JSP中插入struts-logic标签<logic:iterate id="" name="" >
- 求助兄弟们,一个正则表达式,其中的字符能否替换为数字
获取head信息设置到哪...?
就是下载完后打开有问题,如果是rar,则是:无法预料的压缩文件末端。。
如果是图片,则是:无法预览,甚至只有某一部份是正常的
例如:100K
5个线程读取,那每个分20K
那么thread-1~thread-5分别分派20K,计算好每个线程的起始和结束的位置,
就如迅雷一样。每个线程做自己的事互不干涉
1. 取得这个图片的大小
2. 创建一个图片大小的文件
3. 分配每个线程下载的字节数量,计算出每个线程要写文件的位置
4. 使用RandomAccessFile随机定位,每个线程从自己的位置处开始写入已下载到的数据
、如果你是如果是IE下载,那么只是单线程。如果使用工具下载,那也是多次请求啊。正常来说你不需要自己启动线程的,我还是不明白你的意思
public class DownloadTest {
// 下载的URL地址
private URL url = null;
// 开启的线程数
private int threadNum = 5;
// 每个线程下载的范围
private long subLen = 0;
// 下载的文件总长度
private long fileLength = 0;
// 要下载的目标文件
private String localFile = "e:\\yyy.rar.tag";
// 临时文件记录下载任务的信息
private String localTmp = "e:\\yyy.tmp";
private String range = null;
private HttpServletResponse response = null;
public void download(String urlStr){
// 初始化计数器为开启的线程数
CountDownLatch latch = new CountDownLatch(this.getThreadNum());
// 子线程对象
DemoThread[] demoThread = new DemoThread[this.getThreadNum()];
// 使用线程池管理线程
ExecutorService pool = Executors.newCachedThreadPool();
// 每段线程下载的开始位置
long[] startPos = new long[this.getThreadNum()];
// 每段线程下载的结束位置
long[] endPos = new long[this.getThreadNum()];
BufferedOutputStream output = null;
HttpURLConnection httpConn = null;
InputStream input = null;
try {
this.setUrl(new URL(urlStr));
long begin = 0, end = 0;
// 判断是否有带Range参数
if(null == this.getRange()){
begin = 0;
end = this.getFileLength();
} else {
String Range[] = this.getRange().split(",");
System.out.println("Range[0]:"+Range[0]);
String rangePos[] = Range[0].split("-");
if("".equals(rangePos[0])){
begin = this.getFileLength() - Long.parseLong((rangePos[1].toString()));
}else{
begin = Long.parseLong((rangePos[0].toString()));
}
if(rangePos.length != 1){
if("".equals(rangePos[1])){
end = this.getFileLength();
}else{
end = Long.parseLong((rangePos[1].toString()));
if("".equals(rangePos[0])){
end = this.getFileLength();
}
}
}else{
end = this.getFileLength();
}
}
// 每块线程负责下载的长度
this.setSubLen((end - begin) / this.getThreadNum());
System.out.println("subLen: "+this.getSubLen());
int mod = (int) ((end - begin) % this.getThreadNum());
System.out.println(this.getLocalFile());
String thisFileName = this.getLocalFile().substring(0,this.getLocalFile().length());
//String fileName = thisFileName.substring(4,thisFileName.length());
this.getResponse().setContentType("application/x-msdownload;charset=utf-8");
this.getResponse().reset(); // 清除缓冲中的数据
this.getResponse().setHeader("Content-Disposition","attachment;filename="+thisFileName);
this.getResponse().setHeader("Content-Length",String.valueOf(this.getFileLength()));
this.getResponse().setHeader("Location", this.getUrl().toString());
System.out.println("begin:"+begin+" end:"+end+" mod:"+mod);
long currtime = System.currentTimeMillis();
output = new BufferedOutputStream(this.getResponse().getOutputStream());
// 分多个线程下载文件
for(int i = 0; i < this.getThreadNum(); i ++){
startPos[i] = this.getSubLen() * i ;
if(null == this.getRange()){
if(i == this.getThreadNum() - 1){
endPos[i] = this.getFileLength();
} else {
endPos[i] = this.getSubLen() * (i + 1) -1;
}
}else{
startPos[i] = begin + startPos[i] + i;
if(i == this.getThreadNum() - 1){
endPos[i] = end;
}else{
endPos[i] = startPos[i] + this.getSubLen() ;
}
}
System.out.println("startPos["+i+"]:"+startPos[i]+" endPos["+i+"]:"+endPos[i]);
DemoThread dt = new DemoThread(this, latch, i+1, startPos[i], endPos[i], output);
demoThread[i] = dt;
pool.execute(dt);
}
// 阻塞主线程,等待CountdownLatch信号为0,表示所有子线程都结束。
latch.await();
pool.shutdown();
this.getResponse().flushBuffer();//将缓冲中的数据写入到客户端
System.out.println("time: "+ (System.currentTimeMillis() - currtime) );
mergeFile();
} catch (MalformedURLException mue) {
mue.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(null != httpConn) httpConn.disconnect();
if(null != output) output.close();
if(null != input) input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
output = new BufferedOutputStream(this.getResponse().getOutputStream());也是错误的。第二个线程不能用这个output,因为第二个线程必须等待第一个线程全部下载完成并写入output后,第二个线程才可以开始往这个output中写入。如果你非常希望使用多线程,那么你应该为第二个线程至第N个线程传递每一个
OutputStream os = new FileOutputStream(java.io.File.createTempFile("p", "s"));
即每个线程都写入一个文件
然后在download方法中,逐个调用dt.join(),然后再将这个临时File中的内容再写入第一个线程所看到的out中
Future<File>[] callables = new Future[this.getThreadNum()];File file = File.createTempFile("download", "tmp");
DemoThread dt = new DemoThread(this, i+1, startPos[i], endPos[i], new BufferedOutputStream(new FileOutputStream(file)));然后把pool.execute修改为callables[i] = pool.submit(dt, file);删除了latch.await();并增加 for(int i = 0; i < this.getThreadNum(); i ++){
FileInputStream is = new FileInputStream(callables[i].get());
byte[] buff = new byte[4096];
int readed;
while((readed = is.read(buff)) > 0)
output.write(buff, 0, readed);
is.close();
}
output.close();
不也是要去掉...?
1. 取得这个图片的大小
2. 创建一个图片大小的文件
3. 分配每个线程下载的字节数量,计算出每个线程要写文件的位置
4. 使用RandomAccessFile随机定位,每个线程从自己的位置处开始写入已下载到的数据