1.createzipԴ??
2.phpä¸è½½å®ç½ï¼
3.c#压缩解压 文件夹
createzipԴ??
文件处理一直都是前端人的心头病,如何控制好文件大小,文件太大上传不了,文件下载时间太长,tcp直接给断开了?等效果为了方便大家有意义的学习,这里就先放效果图,网页展示源码如果不满足直接返回就行,不浪费大家的时间。
文件上传文件上传实现,分片上传,暂停上传,恢复上传,文件合并等
文件下载为了方便测试,好客系统源码我上传了1个1g的大文件拿来下载,前端用的是流的方式来保存文件的,具体的可以看这个apiTransformStream
正文本项目的地址是:/post/
requestIdleCallback有不明白的可以看这里:/post/
接下来咋们来计算文件的hash,计算文件的hash需要使用spark-md5这个库,
全量计算文件hashexportasyncfunctioncalcHashSync(file:File){ //对文件进行分片,每一块文件都是分为2MB,这里可以自己来控制constsize=2**;letchunks:any[]=[];letcur=0;while(cur<file.size){ chunks.push({ file:file.slice(cur,直尺公式源码cur+size)});cur+=size;}//可以拿到当前计算到第几块文件的进度lethashProgress=0returnnewPromise(resolve=>{ constspark=newSparkMD5.ArrayBuffer();letcount=0;constloadNext=(index:number)=>{ constreader=newFileReader();reader.readAsArrayBuffer(chunks[index].file);reader.onload=e=>{ //累加器不能依赖index,count++;//增量计算md5spark.append(e.target?.resultasArrayBuffer);if(count===chunks.length){ //通知主线程,计算结束hashProgress=;resolve({ hashValue:spark.end(),progress:hashProgress});}else{ //每个区块计算结束,通知进度即可hashProgress+=/chunks.length//计算下一个loadNext(count);}};};//启动loadNext(0);});}全量计算文件hash,在文件小的时候计算是很快的,但是在文件大的情况下,计算文件的汇率支付源码hash就会非常慢,并且影响主进程哦?
抽样计算文件hash抽样就是取文件的一部分来继续,原理如下:
/***抽样计算hash值大概是1G文件花费1S的时间**采用抽样hash的方式来计算hash*我们在计算hash的时候,将超大文件以2M进行分割获得到另一个chunks数组,*第一个元素(chunks[0])和最后一个元素(chunks[-1])我们全要了*其他的元素(chunks[1,2,3,4....])我们再次进行一个分割,这个时候的分割是一个超小的大小比如2kb,我们取*每一个元素的兔子指标源码头部,尾部,中间的2kb。*最终将它们组成一个新的文件,我们全量计算这个新的文件的hash值。*@paramfile{ File}*@returns*/exportasyncfunctioncalcHashSample(file:File){ returnnewPromise(resolve=>{ constspark=newSparkMD5.ArrayBuffer();constreader=newFileReader();//文件大小constsize=file.size;letoffset=2**;letchunks=[file.slice(0,offset)];//前面2mb的数据letcur=offset;while(cur<size){ //最后一块全部加进来if(cur+offset>=size){ chunks.push(file.slice(cur,cur+offset));}else{ //中间的前中后去两个字节constmid=cur+offset/2;constend=cur+offset;chunks.push(file.slice(cur,cur+2));chunks.push(file.slice(mid,mid+2));chunks.push(file.slice(end-2,end));}//前取两个字节cur+=offset;}//拼接reader.readAsArrayBuffer(newBlob(chunks));//最后Kreader.onload=e=>{ spark.append(e.target?.resultasArrayBuffer);resolve({ hashValue:spark.end(),progress:});};});}这个设计是不是发现挺灵活的,真是个人才哇
在这两个的基础上,咋们还可以分别使用web-worker和requestIdleCallback来实现,源代码在hereヾ(≧▽≦*)o
这里把我电脑配置说一下,公司给我分的电脑配置比较lower,8g内存的老机器。计算(3.3g文件的)hash的结果如下:
结果很显然,全量无论怎么弄,都是比抽样的更慢。
文件分片的方式这里可能大家会说,文件分片方式不就是等分吗,其实还可以根据网速上传的速度来实时调整分片的大小哦!
consthandleUpload1=async(file:File)=>{ if(!file)return;constfileSize=file.sizeletoffset=2**letcur=0letcount=0//每一刻的大小需要保存起来,方便后台合并constchunksSize=[0,2**]constobj=awaitcalcHashSample(file)as{ hashValue:string};fileHash.value=obj.hashValue;//todo判断文件是否存在存在则不需要上传,也就是秒传while(cur<fileSize){ constchunk=file.slice(cur,cur+offset)cur+=offsetconstchunkName=fileHash.value+"-"+count;constform=newFormData();form.append("chunk",chunk);form.append("hash",chunkName);form.append("filename",file.name);form.append("fileHash",fileHash.value);form.append("size",chunk.size.toString());letstart=newDate().getTime()//todo上传单个碎片constnow=newDate().getTime()consttime=((now-start)/).toFixed(4)letrate=Number(time)///速率有最大和最小可以考虑更平滑的过滤比如1/tanif(rate<0.5)rate=0.5if(rate>2)rate=2offset=parseInt((offset/rate).toString())chunksSize.push(offset)count++}//todo可以发送合并操作了} ATTENTION!!!?如果是这样上传的文件碎片,如果中途断开是无法续传的(每一刻的网速都是不一样的),除非每一次上传都把chunksSize(分片的数组)保存起来哦控制/post/phpä¸è½½å®ç½ï¼
phpwebæä¹ä¸è½½
1ãå¨æµè§å¨ä¸æç´¢PHPï¼è¿å ¥phpå®ç½ã
2ãç¹å»Downloadsé项ï¼è¿å ¥ä¸è½½é¡µé¢ã
3ãéæ©ä¸ä¸ªéåçPHPçæ¬ï¼ç¶åç¹å»é¡µé¢ä¸çWindowsdownloadsã
4ãç¹å»zipæé®ï¼åç¹å»ä¸è½½æé®å³å¯ã
å¦ä½å¨æ¬å°æºä¸çphpåå¤å·¥ä½é¦å è¦ä¸è½½å¦ä¸è½¯ä»¶ï¼æ¨èå°å®ç½ä¸è½½ï¼å¦æä½ æ¯åæä¸æ ·åªæ¯æµè¯ï¼é£ä¹å°skycn.comä¸è½½å§ï¼è¿æ ·æ¯è¾å¿«ã
Apache
ææµè¡çHTTPæå¡å¨è½¯ä»¶ä¹ä¸ãå¿«éãå¯é ãå¯éè¿ç®åçAPIæ©å±ï¼Perl/Python解éå¨å¯è¢«ç¼è¯å°æå¡å¨ä¸ï¼å®å ¨å è´¹ï¼å®å ¨æºä»£ç å¼æ¾ã
æä¸è½½çæ¯forWindowsçæ¬ï¼ç®åææ°çæ¬æ¯ï¼2.0.
PHP
PHPæ¯ä¸ç§HTMLå åµå¼çè¯è¨ãèPHPç¬ç¹çè¯æ³æ··åäºCãJavaãPerl以åPHPå¼çæ°è¯æ³ãå®å¯ä»¥æ¯CGIæè Perlæ´å¿«éçæ§è¡å¨æç½é¡µã
æä¸è½½çæ¯forWindowsçæ¬ï¼ç®åææ°çæ¬æ¯ï¼5.0.4
MySQL
æ¯ä¸ä¸ªå¤çº¿ç¨çï¼ç»æåæ¥è¯¢è¯è¨(SQL)æ°æ®åºæå¡å¨ãSQLå¨ä¸çä¸æ¯ææµè¡çæ°æ®åºè¯è¨ãMySQLçæ§è¡æ§è½é常é«ï¼è¿è¡é度é常快ï¼å¹¶é常容æ使ç¨ã
æä¸è½½çæ¯forWindowsçæ¬ï¼ç®åææ°çæ¬æ¯ï¼5.0.4Beta
phpMyAdmin
phpMyAdminæ¯ä¸ä¸ªç¨PHPç¼åçï¼å¯ä»¥éè¿äºèç½æ§å¶åæä½MySQLãéè¿phpMyAdminå¯ä»¥å®å ¨å¯¹æ°æ®åºè¿è¡æä½ã
æä¸è½½çæ¯forWindowsçæ¬ï¼ç®åææ°çæ¬æ¯ï¼2.6.2-pl1
å®è£ apacheåPHP
ä¸é¢æ¯æçå®è£ è®°å½ï¼
éæ©ç«¯å£ï¼å°apacheå®è£ å¨e:\apacheä¸ã
é ç½®apacheéçmendedï¼éå½å为php.ini并æ·è´å°windowsç®å½ä¸ã
NTFSä¸è®°å¾ç»æå¡å¨å¼PHP.iniç读æéã
æ¥æ¾extension_diråé¢çæ¹ä¸ºe:/php/ext
æ¥æ¾WindowsExtensionsï¼æä¸é¢æä¸æç;extension=php_***.dllçåå·å»æï¼å°±æ¯æ¯æç»ä»¶äºãææextension=php_gd2.dllextension=php_mbstring.dllextension=php_mysql.dllçåå·å»æäºã
php5é»è®¤ä¸æ¯æmysqläº,æ以è¦ä¸ºä»æ·»å æ¯æ,é¤äºextension=php_mysql.dllå»åå·ä¹å¤ï¼å¨phpç®å½éæ个libmysql.dllæ件,æå®å¤å¶å°ç³»ç»çsystemæ件夹ä¸,å¤å¶php.iniå°windowsç®å½ä¸ã
æ¤æ¶PHPç¯å¢åºæ¬å·²ç»é ç½®æå
å¨WEBæ ¹ç®å½é建ä¸ä¸ªå为test.phpçæ件å 容å¦ä¸
echophpinfo();?éæ°å¯å¨apacheæå¡ï¼ç¨æµè§å¨æå¼
å¦æå¯ä»¥çå°phpé ç½®è¾åºä¿¡æ¯å°±æåäº
å®è£ mysql
å°mysqlå®è£ å°æå®ç®å½ï¼ç¶åå®è£ ç¨åºä¼å¼å¯¼ä½ ä¸æ¥æ¥é ç½®ãä¸è¿å¥æªçæ¯æåä»»å¡æ 没æåºç°Mysqlçå¾æ ã
ä¿®æ¹mysqlæ°æ®åºçrootå¯ç
ç¨cmdè¿å ¥å½ä»è¡æ¨¡å¼è¾å ¥å¦ä¸å½ä»¤:(注:d:\mysql为mysqlå®è£ ç®å½)
cdd:\mysql\bin
mysqladmin-uroot-ppassword
å车åºç°
Enterpassword:(注:è¿æ¯å«ä½ è¾å ¥åå¯ç .åå®è£ æ¶å¯ç 为空,æ以ç´æ¥å车å³å¯)
æ¤æ¶mysqlä¸è´¦å·rootçå¯ç 被æ¹ä¸ºå®è£ å®æ¯
é ç½®php.ini并æµè¯mysql
æ¾å°extension_dir=./æ¹ä¸ºextension_dir=e:/php/ext
æ¾å°
;extension=php_mysql.dll
å°';'å»ææ¹ä¸º
extension=php_mysql.dll
æ¾å°
;session.save_path=/tmp
å°';'å»æè®¾ç½®ä½ ä¿åsessionçç®å½ï¼å¦
session.save_path=e:/php/session_temp
éå¯apacheæå¡
å¨Webæ ¹ç®å½ä¸å»ºç«testdb.phpæ件å 容å¦ä¸ï¼
php$link=mysql_connect('localhost','root','');
if(!$link)echofail;
elseechosuccess;
mysql_close();
ç¨æµè§å¨æå¼å¦æè¾åºsuccesså°±OKäº
phpmyadminçå®è£ é ç½®
å°phpMyAdmin-V2.6.2-pl1.zip解åå°WEBæ ¹ç®å½ä¸å»ï¼éå½åæ件夹为phpmyadminæå ¶å®
æå¼phpmyadminç®å½ä¸çconfig.inc.php
æ¾å°
$cfg['Servers'][$i]['user']='root';
$cfg['Servers'][$i]['password']='';
åå«å¡«ä¸ç¨æ·ååå¯ç
linuxphpç½ç«æä¹å®è£
ä¸ãå®è£ Apache2.2.
1ãå°å®ç½ä¸è½½
2ã解å
tar-zxvfpilerfoundin$Path
ç´æ¥è¿è¡yuminstallgccï¼å®è£ Gccå³å¯
第ä¸ä¸ªé®é¢ä¸ºï¼pcre-configforlibpcrenotfound
解å³æ¹æ³å°±æ¯ä¸è½½prceå®è£ å ï¼åAPR类似ï¼å®è£ å°/usr/local/pcreæ件夹ä¸é¢å³å¯ãPS:fedoraä¸å®è£ c++ç¼è¯å¨g++çå½ä»¤ä¸ºï¼yuminstallgcc-c++ã
5ãç¼è¯
make
6ãå®è£
makeinstall
7ãå¯å¨ï¼éå¯ååæ¢ï¼å åæ¢å°å®è£ å®æåçç®å½/usr/local/apache2/bin
./apachectl-kstart
./apachectl-krestart
./apachectl-kstop
8ãé ç½®æ件(满足æåºæ¬çé ç½®)
ç¼è¾/usr/local/apache2/conf/press.Z
AddTypeapplication/x-gzip.gz.tgz
å¨åé¢æ·»å ï¼
AddTypeapplication/x-pression
foreach (string file in filenames)
{
//打开压缩文件
FileStream fs = File.OpenRead(file);
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
ZipEntry entry = new ZipEntry(file);
entry.DateTime = DateTime.Now;
// set Size and the crc, because the information
// about the size and crc should be stored in the header
// if it is not set it is automatically written in the footer.
// (in this case size == crc == -1 in the header)
// Some ZIP programs have problems with zip files that don't store
// the size and crc in the header.
entry.Size = fs.Length;
fs.Close();
crc.Reset();
crc.Update(buffer);
entry.Crc = crc.Value;
s.PutNextEntry(entry);
s.Write(buffer, 0, buffer.Length);
}
s.Finish();
s.Close();
}
}
}
现在再来看看解压文件类的源码
/// <summary>
/// 解压文件
/// </summary>
using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.Data;
using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
namespace DeCompression
{
public class UnZipClass
{
public void UnZip(string[] args)
{
ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]));
ZipEntry theEntry;
while ((theEntry = s.GetNextEntry()) != null)
{
string directoryName = Path.GetDirectoryName(args[1]);
string fileName = Path.GetFileName(theEntry.Name);
//生成解压目录
Directory.CreateDirectory(directoryName);
if (fileName != String.Empty)
{
//解压文件到指定的目录
FileStream streamWriter = File.Create(args[1]+theEntry.Name);
int size = ;
byte[] data = new byte[];
while (true)
{
size = s.Read(data, 0, data.Length);
if (size > 0)
{
streamWriter.Write(data, 0, size);
}
else
{
break;
}
}
streamWriter.Close();
}
}
s.Close();
}
}
}
有了压缩和解压缩的类以后,就要在窗体里调用了。怎么?是新手,不会调用?Ok,接着往下看如何在窗体里调用。
首先在窗体里放置两个命令按钮(不要告诉我你不会放啊~),然后编写以下源码
/// <summary>
/// 调用源码
/// </summary>
private void button2_Click_1(object sender, System.EventArgs e)
{
string []FileProperties=new string[2];
FileProperties[0]="C:\\unzipped\\";//待压缩文件目录
FileProperties[1]="C:\\zip\\a.zip"; //压缩后的目标文件
ZipClass Zc=new ZipClass();
Zc.ZipFileMain(FileProperties);
}
private void button2_Click(object sender, System.EventArgs e)
{
string []FileProperties=new string[2];
FileProperties[0]="C:\\zip\\test.zip";//待解压的文件
FileProperties[1]="C:\\unzipped\\";//解压后放置的目标目录
UnZipClass UnZc=new UnZipClass();
UnZc.UnZip(FileProperties);
}
好了,到此为止,如何压缩和解压缩的类都已经完成了,需要的朋友直接拿走调吧。