基于Java多线程的下载器源码剖析_第1页
基于Java多线程的下载器源码剖析_第2页
基于Java多线程的下载器源码剖析_第3页
基于Java多线程的下载器源码剖析_第4页
基于Java多线程的下载器源码剖析_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 PAGE25 / NUMPAGES25HYPERLINK :/zhzhl202/article/details/7521377基于Java多线程的下载器源码剖析(一)分类:HYPERLINK :/zhzhl202/article/category/1136415JavaHYPERLINK :/zhzhl202/article/category/1136415多线程2012-04-28 21:29747人阅读HYPERLINK :/zhzhl202/article/details/7521377评论(6)HYPERLINK javascript:void(0);收藏HYPERLINK :/zh

2、zhl202/article/details/7521377举报HYPERLINK :/tag/details.html?tag=%e5%a4%9a%e7%ba%bf%e7%a8%8b多线程HYPERLINK :/tag/details.html?tag=javajavaHYPERLINK :/tag/details.html?tag=downloaddownloadHYPERLINK :/tag/details.html?tag=bytebyteHYPERLINK :/tag/details.html?tag=filefileHYPERLINK :/tag/details.html?tag=

3、exceptionexception目录(?)HYPERLINK :/zhzhl202/article/details/7521377+本文实现了一个基于Java多线程的下载器,可提供的功能有:1. 对文件使用多线程下载,并显示每时刻的下载速度。2. 对多个下载进行管理,包括线程调度,存管理等。这篇文章的结构如下:首先讨论如何实现利用Java多线程对单个文件进行下载。然后讨论当系统中有多个文件下载,如何对这些下载进行管理。包括线程调度,存管理等。一:单个文件下载的管理1. 单文件下载类层次首先简要介绍一下单个文件下载管理的类层次:来一图来表示。为需要下载的文件创建一个Download类,Dow

4、nload负责管理该文件下载时的线程管理、文件管理、当前速度计算等操作。根据线程的数目tNum,将该文件分为tNum段,每段为一个DownloadBlock。在实际下载的过程中,并不是一次把所有的东西下载完,而是每次下载固定size的一段Di。所以每个DownloadBlock又会分成n段。为每个DownloadBlock申请一个线程DownloadThread。其主要作用就是每次下载一段Di,并将其写入到文件中。2. 单文件下载对于单个下载,步骤如下连接资源服务器,获取资源信息,创建文件切分资源,为每个线程分配固定的下载区域。1)封装下载的属性在建立下载之前,我们把每一次下载进行抽象封装。首

5、先把URL、目标文件等封装在一个DownloadConfig类中。其中包含了4个属性:javaHYPERLINK :/zhzhl202/article/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copyprivateURLurl;/文件下载地址privateFilefile;/下载文件保存目标文件privateintnthread;/下载该文件需要的线程数privateintpriority;/该下载的优先级如下如所示:2)连接资源服务器,获取资源信息,创建文件,并指定文件大小javaHYPERLI

6、NK :/zhzhl202/article/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copylength=config.getUrl().openConnection().getContentLength();RandomAccessFilefile=newRandomAccessFile(config.getFile(),rw);file.setLength(length);file.close();3)切分资源,为每个线程分配固定的下载区域,并将当前的下载加入到队列中javaHYPERLINK

7、 :/zhzhl202/article/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copyintsize=length/config.getNthread();for(inti=0;iconfig.getNthread();i+)intstart=i*size;intlen;if(i=config.getNthread()-1)len=length-start;elselen=size;/并将当前的下载加入到下载队列中addDownloadBlock(getDownloadBlock(start,l

8、en);3)启动线程进行下载下载的步骤如下:1. 创建缓存,创建连接。设置获取资源数据的围,创建文件,并设置写入位置javaHYPERLINK :/zhzhl202/article/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copy/创建缓存byteb;if(block.getLength()Constants.BYTES_READ)b=newbyte(int)block.getLength();elseb=newbyteConstants.BYTES_READ;/创建连接。设置获取资源数据的围,从

9、startPos到endPosURLConnectioncon=null;con.setRequestProperty(Range,bytes=+block.getStart()+-+block.getStart()+block.getLength()-1);RandomAccessFilefile=newRandomAccessFile(block.getDownload().getConfig().getFile(),rw);/创建RandomAccessFilefile.seek(block.getStart();/从startPos开始写入 2. 如果当前block的length大于0

10、,则从URL资源处获取固定大小的资源,并将其写入到文件中。 3 .更新block块的start,以与length,如果length大于0,继续进行2,否则则表示当前block已经下载完毕,退出该线程。javaHYPERLINK :/zhzhl202/article/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copyInputStreamin=block.getDownload().getConfig().getUrl().openStream();intn;/对该block的文件进行下载,while

11、(countblock.getLength()if(needSplit()/检查该Block是否还需要分块(即当前block剩余的大小大于一次下载的量)longnewLength=(block.getLength()-count)/2;longnewStart=block.getStart()+block.getLength()-newLength;DownloadBlocknewBlock=block.getDownload().getDownloadBlock(newStart,newLength);block.setLength(block.getLength()-newLength);

12、block.getDownload().addDownloadBlock(newBlock);/写入文件n=in.read(b);if(nblock.getLength()file.write(b,0,(int)(block.getLength()-count);count=block.getLength();elsecount+=n;file.write(b,0,n);/setblockcountindownloadif(n0)/统计每个block中已经下载的段的个数,用于计算当前下载的速度。block.getDownload().setBlockCount(block.getStart()

13、,count);in.close();file.close();二 . 当前文件下载速度与进度计算如第一个图所表示的,每个Block中又分为了很多的段D1、D2、Dn,因此当为了计算当前下载的速度,需要将下载的段D的数量统计出来,这里使用了一个ConcurrentHashMap来保存每个block已经下载完成的段D的数目。其中key为每个block的start值,而value为该block已经下载完的段D。在当前时刻,我们需要统计当前Download已经下载完成段D的数量,然后再和上一时刻的相比较,则可以得出当前的下载速度。具体代码见下:javaHYPERLINK :/zhzhl202/art

14、icle/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copyclassCheckSpeedTaskextendsTimerTaskprivatestaticfinalLoglog=LogFactory.getLog(CheckSpeedTask.class);privateDownloaddownload;privateConcurrentHashMapblockCounts;privatelongspeed=0;/Byte/Sprivatelongcount=0;/Totaldownloadedb

15、ytecountprivatelonglastCount=0;privatelongtime=0;/ChecktimeprivatelonglastTime=0;publicCheckSpeedTask(Downloaddownload,longstartTime,ConcurrentHashMapblockCounts)this.download=download;this.lastTime=startTime;this.blockCounts=blockCounts;Overridepublicvoidrun()trytime=System.currentTimeMillis();coun

16、t=0;/需要统计当前已经下载完成段D的数量。for(longc:blockCounts.values()count+=c;speed=(count-lastCount)/(time-lastTime)/1000);log.debug(blockCounts.size()+threadsaredownloading+download+,cuttentis+speed+Byte/S,+(count*1.0)/download.getLength()*100+%downloaded);download.setCount(count);download.setSpeed(speed);lastTim

17、e=time;lastCount=count;catch(Exceptione)/TODO:handleexceptione.printStackTrace();这样我们就可以在Thread类的run()函数中,计算当前下载的速度javaHYPERLINK :/zhzhl202/article/details/7521377view plainHYPERLINK :/zhzhl202/article/details/7521377copywhile(activeThreads.size()0|blockQueue.size()0)Thread.sleep(1000);checkSpeed();

18、上面的代码演示了如何使用Java多线程对单个文件进行下载,接下来我们继续讨论如何对多个下载进行调度、管理等HYPERLINK :/zhzhl202/article/details/7521940基于Java多线程的下载器源码剖析(二)分类:HYPERLINK :/zhzhl202/article/category/1136415JavaHYPERLINK :/zhzhl202/article/category/1136415多线程2012-04-29 10:36571人阅读HYPERLINK :/zhzhl202/article/details/7521940评论(6)HYPERLINK ja

19、vascript:void(0);收藏HYPERLINK :/zhzhl202/article/details/7521940举报HYPERLINK :/tag/details.html?tag=%e5%a4%9a%e7%ba%bf%e7%a8%8b多线程HYPERLINK :/tag/details.html?tag=javajavaHYPERLINK :/tag/details.html?tag=downloaddownloadHYPERLINK :/tag/details.html?tag=integerintegerHYPERLINK :/tag/details.html?tag=th

20、readthreadHYPERLINK :/tag/details.html?tag=timertimer目录(?)HYPERLINK :/zhzhl202/article/details/7521940+三:多个文件下载的管理这一节我们主要来讲一下如何对多个文件的下载进行管理首先来看一下整个系统的UML图从最下面开始说起:Download代表一个下载类,对每一个文件都需要创建一个Download实例,用于对该文件下载线程的管理。其中每个Download中都有以下几个对象:javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYP

21、ERLINK :/zhzhl202/article/details/7521940copyprivateConcurrentLinkedQueueblockQueue;privateConcurrentLinkedQueueblockCache;privateConcurrentHashMapblockCounts;privateConcurrentLinkedQueueactiveThreads;其中blockQueue是一个队列,用于存储当前需要下载的DownloadBlock。Download对文件进行切分形成的DownloadBlock会被放入到放入到blockQueue中,供以后的下

22、载。blockCache为block存缓存池,主要是为了能够复用已经建立好的DownloadBlock。blockCounts为一个Map,其中key为每个block的start值,而value为该block已经下载完的段D。主要作用是统计出当前已经每个Block已经下载完的段D,以计算实时下载速度activeThreads 主要是为了存储该Thread中所有的活跃线程。DownloadBlock是一个下载块,其里面有3个成员变量javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/arti

23、cle/details/7521940copyprivateDownloaddownload;/其所属的Downloadprivatelongstart;/下载文件起始处privatelonglength;/下载文件的长度DownloadThread是指下载进程,每个DownloadBlock都需要启动一个DownloadThread去进行下载。即javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copynewDownloadThread(b

24、lock).start()DownloadDeamon为了一个守护线程。其部主要为了下载所有的需要下载DownloadBlockjavaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copyprivateDownloadListdownloads;/当前系统中所有的下载列表privateExecutorServicethreadPool;/线程池Downloader 代表整个下载系统,整个系统中只有一个实例对象,因此我们需要保证系统中只有一个实例

25、对象。javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copyprivateDownloaderConfigconfig;/Downloader配置privateDownloadListdownloads;/当前系统所有的下载列表privateThreaddeamon;/守护进程privateConcurrentLinkedQueueblockCache;/当前系统的缓存privateTimertimer;/看了上面一大堆的东西,我保证你现

26、在很晕,OK,我们从使用的角度来看整个系统是如何运行的。下面是示例代码。javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copypublicstaticvoidmain(Stringargs)Downloaderdownloader=Downloader.getInstance();/下载第一个文件Stringurl1= s:/tmsvm.googlecode./files/tmsvm_src_v1.1.0.rar;StringsaveFi

27、le1=data/tmsvm_src_v1.1.0.rar;DownloadConfigconfig=newDownloadConfig();tryconfig.setUrl(newURL(url1);config.setFile(newFile(saveFile1);config.setNthread(newInteger(5);config.setPriority(newInteger(6);/将第一个下载加入到下载列表中downloader.addDownload(newDownload(config,downloader.getTimer();catch(MalformedURLExc

28、eptione)/TODOAuto-generatedcatchblocke.printStackTrace();/下载第二个文件Stringurl2= s:/tmsvm.googlecode./files/Tmsvm%E5%8F%82%E8%80%83%E6%96%87%E6%A1%A3%28v1.1.0%29.rar;StringsaveFile2=data/Tmsvm参考文档(v1.1.0).rar;tryconfig.setUrl(newURL(url2);config.setFile(newFile(saveFile2);config.setNthread(newInteger(5)

29、;config.setPriority(newInteger(6);/将第二个下载加入到下载列表中downloader.addDownload(newDownload(config,downloader.getTimer();catch(MalformedURLExceptione)/TODOAuto-generatedcatchblocke.printStackTrace();1. 系统初始化首先来看这一行行:javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/detai

30、ls/7521940copyDownloaderdownloader=Downloader.getInstance();Downloader是这个下载器的总调度师,一山不容二虎,当然在系统运行过程中,只能有一个Downloader的实例,因此我们需要用单例模式来保证这一点。首先要取得downloader实例,即系统的初始化。我们看系统初始化需要做什么?javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copypublicstaticDownl

31、oadergetInstance()if(downloader=null)downloader=newDownloader(newDownloaderConfig();returndownloader;privateDownloader(DownloaderConfigconfig)super();this.config=config;start();上面的代码中的start()函数中到底做了什么呢?初始化blockCache缓存,其中blockCache为ConcurrentLinkedQueue类型。启动守护进程DownloadDeamon具体代码如下:javaHYPERLINK :/zh

32、zhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copyprivatevoidstart()blockCache=newConcurrentLinkedQueue();/初始化缓存downloads=newDownloadList();deamon=newThread(newDownloadDeamon(downloads);/初始化守护进程deamon.setDaemon(true);deamon.start();timer=newTimer(Constants.TIMER

33、_NAME,true);上面代码中启动了一个守护进程。那么这个守护进程在启动的时候在做什么事情呢?我们来看一下他的run()函数javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copypublicvoidrun()System.out.println(Createthreadpool);threadPool=Executors.newCachedThreadPool();/初始化线程池DownloadBlockblock;while(tru

34、e)block=getDownloadBlock();/不断从当前系统中获取待下载的DownloadBlockif(block!=null)(Createnewdownloadthreadfor+block);/启动线程执行下载threadPool.execute(newDownloadThread(block);/将当前Block从其所在的Download中移除block.getDownload().removeDownloadBlock(block);tryThread.sleep(1000);catch(InterruptedExceptione)/TODOAuto-generatedc

35、atchblockSystem.out.println(Downloaddeamonstopedbyuser);break;守护进程所做的事情就是不断获取将要进行下载的Block,然后启动线程去进行下载。来看一下获取Block的策略:这里不断的从当前下载列表中获取所有的Download,然后从里面选取最需要下载的文件,“最需要下载”定义为剩余的待下载量最多。其具体的代码看下方:javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copypriv

36、ateDownloadBlockgetDownloadBlock()downs=downloads.toArray();if(downs=null|downs.length=0)returnnull;Downloaddownload=downs0;for(Downloaddown:downs)/找最需要下载的Download进行下载if(down.getRemainThread()download.getRemainThread()download=down;download.descRemainThread();returndownload.getDownloadBlock();2. 新建下

37、载上面讲解的是系统初始化所做的事情。那么当我们把开始下载一个文件时系统是怎么运行的呢?javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copy/下载第一个文件Stringurl1= s:/tmsvm.googlecode./files/tmsvm_src_v1.1.0.rar;StringsaveFile1=data/tmsvm_src_v1.1.0.rar;DownloadConfigconfig=newDownloadConfig();

38、tryconfig.setUrl(newURL(url1);config.setFile(newFile(saveFile1);config.setNthread(newInteger(5);config.setPriority(newInteger(6);/将第一个下载加入到下载列表中downloader.addDownload(newDownload(config,downloader.getTimer();catch(MalformedURLExceptione)/TODOAuto-generatedcatchblocke.printStackTrace();我们重点来看这一句:java

39、HYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copy/将第一个下载加入到下载列表中downloader.addDownload(newDownload(config,downloader.getTimer();addDownload的定义如下javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940c

40、opypublicbooleanaddDownload(Downloaddownload)newThread(download).start();returndownloads.add(download);这段代码做了两件事情:为Download启动一下线程。Download线程所做的事情就是把当前的文件根据线程数目进行切分。把当前Download加入到DownloadList中。OK,我们看看,Download是切分文件时是如何与整个系统联系在一起。javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zh

41、zhl202/article/details/7521940copypublicvoidrun()trybegin=System.currentTimeMillis();/getlength(Begindownload+config.getUrl();length=config.getUrl().openConnection().getContentLength();(Totalsize:+length);/createfile(Createfile+config.getFile();RandomAccessFilefile=newRandomAccessFile(config.getFile

42、(),rw);file.setLength(length);(Createdwithlength=+length);file.close();intsize=length/config.getNthread();/addinitialblockslog.debug(Addinitial+config.getNthread()+downloadblocks);for(inti=0;i0|blockQueue.size()0)Thread.sleep(1000);checkSpeed();/stopthetaskscheckSpeedTask.cancel();splitBlockTask.can

43、cel();longtotal=System.currentTimeMillis()-begin;speed=length/(total/1000);(Completedownload+config.getUrl()+n+Totaltime:+total+ms+n+Averagespeed:+speed+Byte/s);log.debug(this+putallblockinblockCachebacktodownloadersystem);for(DownloadBlockblock:blockCache)Downloader.getInstance().putDownloadBlock(b

44、lock);catch(FileNotFoundExceptione)/TODOAuto-generatedcatchblocke.printStackTrace();catch(IOExceptione)/TODOAuto-generatedcatchblocke.printStackTrace();catch(InterruptedExceptione)/TODOAuto-generatedcatchblocke.printStackTrace();上面的代码,我们在第一篇中已经讲解过了,这里我们会重点看javaHYPERLINK :/zhzhl202/article/details/75

45、21940view plainHYPERLINK :/zhzhl202/article/details/7521940copyaddDownloadBlock(getDownloadBlock(start,len);其意思是将当前的切分出来的Block放入到待下载队列中去。而我们在守护进程那里,看到他不断的会从当前系统中找最需要下载的Download,然后再从Download中取出下载队列的Block进行下载javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details

46、/7521940copy/DownloadDemen不断的获取DownloadBlockwhile(true)block=getDownloadBlock();if(block!=null)System.out.println(Createnewdownloadthreadfor+block);threadPool.execute(newDownloadThread(block);block.getDownload().removeDownloadBlock(block);javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYP

47、ERLINK :/zhzhl202/article/details/7521940copy/getDownloadBlock()定义如下:privateDownloadBlockgetDownloadBlock()downs=downloads.toArray();if(downs=null|downs.length=0)returnnull;Downloaddownload=downs0;for(Downloaddown:downs)if(down.getRemainThread()download.getRemainThread()download=down;download.descRe

48、mainThread();returndownload.getDownloadBlock();javaHYPERLINK :/zhzhl202/article/details/7521940view plainHYPERLINK :/zhzhl202/article/details/7521940copy/而download.getDownloadBlock()定义如下所示:publicDownloadBlockgetDownloadBlock()returnblockQueue.peek();写到这里,整个的系统框架目录就非常清晰了:Downloader, DownloadDemen, Do

49、wnload 之间是通过DownloadBlock联系起来的。当有一个文件需要下载时,Downloader 把该Download加入到DownloadList中。而Download自身会通过切分文件创建出多个DownloadBlock。DownloadDemen每时每刻都在获取DownloadBlock,赋予其线程进行下载。下一节,我们会重点讲解一下Downloader如何系统中的缓存进行处理的。HYPERLINK :/zhzhl202/article/details/7522074基于Java多线程的下载器源码剖析(三)分类:HYPERLINK :/zhzhl202/article/cate

50、gory/1136415JavaHYPERLINK :/zhzhl202/article/category/1136415多线程2012-04-29 11:29657人阅读HYPERLINK :/zhzhl202/article/details/7522074评论(12)HYPERLINK javascript:void(0);收藏HYPERLINK :/zhzhl202/article/details/7522074举报HYPERLINK :/tag/details.html?tag=%e5%a4%9a%e7%ba%bf%e7%a8%8b多线程HYPERLINK :/tag/details.

51、html?tag=javajavaHYPERLINK :/tag/details.html?tag=downloaddownloadHYPERLINK :/tag/details.html?tag=%e4%bb%bb%e5%8a%a1任务HYPERLINK :/tag/details.html?tag=concurrencyconcurrencyHYPERLINK :/tag/details.html?tag=%e7%bd%91%e7%bb%9c网络目录HYPERLINK :/zhzhl202/article/details/7522074(?)HYPERLINK :/zhzhl202/art

52、icle/details/7522074+四:缓存管理在Thread切分文件时,会创建非常多的DownloadBlock,为了减少创建、销毁Block所带来对效率上的影响,我们会把已经创建好的DownloadBlock放入到缓存中,当Download需要DownloadBlock时直接从缓存中取得。即我们使用缓存来对已经申请的存重复利用。在每个Download类中都有一个缓存池,即ConcurrentLinkedQueue blockCache;而整个的系统Downloader也会有一个大的ConcurrentLinkedQueue blockCache;那么这两者是怎样的关系呢?在Downl

53、oad类对文件进行切分的时候,需要创建DownloadBlock,而DownloadBlock是根据getDownloadBlock函数来获取到的。javaHYPERLINK :/zhzhl202/article/details/7522074view plainHYPERLINK :/zhzhl202/article/details/7522074copy/*从blockcache获取一块DownloadBlock。*cache中的block都是已经下载完毕的。*paramstartblock块的下载开始位置*paramlengthblock块需要下载的长度*return返回一个创建好的bl

54、ock块。*/publicDownloadBlockgetDownloadBlock(longstart,longlength)DownloadBlockblock;/先从当前的Download中看是否有if(blockCache.size()0)block=blockCache.poll();elseblock=Downloader.getInstance().getDownloadBlock();block.setDownload(this);block.setStart(start);block.setLength(length);returnblock;上面的代码中,首先会从当前Dow

55、nload所拥有的Cache中获取DownloadBlock,如果有空闲的DownloadBlock,则直接取出。如果没有则需要从当前系统中去申请。当当前的DownloadBlock使用DownloadThread下载完毕之后,他所拥有的这个DownloadBlock会交给其所在的Download,以供剩余新建的DownloadBlock使用。javaHYPERLINK :/zhzhl202/article/details/7522074view plainHYPERLINK :/zhzhl202/article/details/7522074copyblock.getDownload().putDownloadBlock(block);而当整个文件都下载完毕之后,需要把当前Download的所有缓存归还给系统(Downloader)javaHYPERLINK :/zhzhl202/article/details/7522074view plainHYPERLINK :/zhzhl202/article/de

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论