diff --git a/packages/client/index.html b/packages/client/index.html index e4c19e8..79240bd 100644 --- a/packages/client/index.html +++ b/packages/client/index.html @@ -8,9 +8,6 @@ 七牛云OSS图床 | 粥里有勺糖 - - diff --git a/packages/client/package.json b/packages/client/package.json index f294718..0afd210 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -24,6 +24,7 @@ "qiniu": "^7.11.0", "qiniu-js": "^3.4.1", "spark-md5": "^3.0.2", + "upng-js": "^2.1.0", "vue": "^3.4.15", "vue-router": "^4.2.5" }, diff --git a/packages/client/public/UPNG.js b/packages/client/public/UPNG.js deleted file mode 100644 index d45ca10..0000000 --- a/packages/client/public/UPNG.js +++ /dev/null @@ -1,1225 +0,0 @@ - - -var UPNG = (function() { - - var _bin = { - nextZero : function(data,p) { while(data[p]!=0) p++; return p; }, - readUshort : function(buff,p) { return (buff[p]<< 8) | buff[p+1]; }, - writeUshort: function(buff,p,n){ buff[p] = (n>>8)&255; buff[p+1] = n&255; }, - readUint : function(buff,p) { return (buff[p]*(256*256*256)) + ((buff[p+1]<<16) | (buff[p+2]<< 8) | buff[p+3]); }, - writeUint : function(buff,p,n){ buff[p]=(n>>24)&255; buff[p+1]=(n>>16)&255; buff[p+2]=(n>>8)&255; buff[p+3]=n&255; }, - readASCII : function(buff,p,l){ var s = ""; for(var i=0; i>3)]>>(7-((i&7)<<0)))& 1), cj=3*j; bf[qi]=p[cj]; bf[qi+1]=p[cj+1]; bf[qi+2]=p[cj+2]; bf[qi+3]=(j>2)]>>(6-((i&3)<<1)))& 3), cj=3*j; bf[qi]=p[cj]; bf[qi+1]=p[cj+1]; bf[qi+2]=p[cj+2]; bf[qi+3]=(j>1)]>>(4-((i&1)<<2)))&15), cj=3*j; bf[qi]=p[cj]; bf[qi+1]=p[cj+1]; bf[qi+2]=p[cj+2]; bf[qi+3]=(j>>3)]>>>(7 -((x&7) )))& 1), al=(gr==tr*255)?0:255; bf32[to+x]=(al<<24)|(gr<<16)|(gr<<8)|gr; } - else if(depth== 2) for(var x=0; x>>2)]>>>(6 -((x&3)<<1)))& 3), al=(gr==tr* 85)?0:255; bf32[to+x]=(al<<24)|(gr<<16)|(gr<<8)|gr; } - else if(depth== 4) for(var x=0; x>>1)]>>>(4 -((x&1)<<2)))&15), al=(gr==tr* 17)?0:255; bf32[to+x]=(al<<24)|(gr<<16)|(gr<<8)|gr; } - else if(depth== 8) for(var x=0; x>1,G=o[i+1],f=y<<4|G,a=j-G,k=o[i]<>>15-j;I[x]=f;k++}}}function g(o,j){var I=D.i,A=15-j;for(var r=0;r>>A}}(function(){var o=1<<15;for(var j=0;j>>1|(I&1431655765)<<1; -I=(I&3435973836)>>>2|(I&858993459)<<2;I=(I&4042322160)>>>4|(I&252645135)<<4;I=(I&4278255360)>>>8|(I&16711935)<<8; -D.i[j]=(I>>>16|I<<16)>>>17}function A(r,i,y){while(i--!=0)r.push(0,y)}for(var j=0;j<32;j++){D.B[j]=D.o[j]<<3|D.z[j]; -D.h[j]=D.p[j]<<4|D.w[j]}A(D.s,144,8);A(D.s,255-143,9);A(D.s,279-255,7);A(D.s,287-279,8);C(D.s,9);t(D.s,9,D.g); -g(D.s,9);A(D.t,32,5);C(D.t,5);t(D.t,5,D.A);g(D.t,5);A(D.b,19,0);A(D.c,286,0);A(D.e,30,0);A(D.a,320,0)}()); -function F(o,j,I){return(o[j>>>3]|o[(j>>>3)+1]<<8)>>>(j&7)&(1<>>3]|o[(j>>>3)+1]<<8|o[(j>>>3)+2]<<16)>>>(j&7)&(1<>>3]|o[(j>>>3)+1]<<8|o[(j>>>3)+2]<<16)>>>(j&7)}function b(o,j){return(o[j>>>3]|o[(j>>>3)+1]<<8|o[(j>>>3)+2]<<16|o[(j>>>3)+3]<<24)>>>(j&7)} -function v(o,j){var I=Uint8Array,r=0,i=0,y=0,G=0,f=0,a=0,k=0,N=0,x=0,P,J; -if(o[0]==3&&o[1]==0)return j?j:new I(0);var A=j==null;if(A)j=new I(o.length>>>2<<3);while(r==0){r=s(o,x,1); -i=s(o,x+1,2);x+=3;if(i==0){if((x&7)!=0)x+=8-(x&7);var K=(x>>>3)+4,m=o[K-4]|o[K-3]<<8;if(A)j=H(j,N+m); -j.set(new I(o.buffer,o.byteOffset+K,m),N);x=K+m<<3;N+=m;continue}if(A)j=H(j,N+(1<<17));if(i==1){P=D.g; -J=D.A;a=(1<<9)-1;k=(1<<5)-1}if(i==2){y=F(o,x,5)+257;G=F(o,x+5,5)+1;f=F(o,x+10,4)+4;x+=14;var O=x,Q=1; -for(var p=0;p<38;p+=2){D.b[p]=0;D.b[p+1]=0}for(var p=0;pQ)Q=l}x+=3*f;C(D.b,Q);t(D.b,Q,D.C);P=D.k;J=D.n;x=B(D.C,(1<>>4;if(L>>>8==0){j[N++]=L}else if(L==256){break}else{var M=N+L-254;if(L>264){var z=D.B[L-257]; -M=N+(z>>>3)+F(o,x,z&7);x+=z&7}var e=J[w(o,x)&k];x+=e&15;var E=e>>>4,c=D.h[E],q=(c>>>4)+s(o,x,c&15);x+=c&15; -if(A)j=H(j,N+(1<<17));while(N>>4;if(f<=15){i[y]=f;y++}else{var a=0,k=0;if(f==16){k=3+F(A,r,2); -r+=2;a=i[y-1]}else if(f==17){k=3+F(A,r,3);r+=3}else if(f==18){k=11+F(A,r,7);r+=7}var N=y+k;while(y>>1;while(ir)r=G;i++}while(i>3, bpl = Math.ceil(w*bpp/8); - var img = new Uint8Array( h * bpl ); - var di = 0; - - var starting_row = [ 0, 0, 4, 0, 2, 0, 1 ]; - var starting_col = [ 0, 4, 0, 2, 0, 1, 0 ]; - var row_increment = [ 8, 8, 8, 4, 4, 2, 2 ]; - var col_increment = [ 8, 8, 4, 4, 2, 2, 1 ]; - - var pass=0; - while(pass<7) - { - var ri = row_increment[pass], ci = col_increment[pass]; - var sw = 0, sh = 0; - var cr = starting_row[pass]; while(cr>3]; val = (val>>(7-(cdi&7)))&1; - img[row*bpl + (col>>3)] |= (val << (7-((col&7)<<0))); - } - if(bpp==2) { - var val = data[cdi>>3]; val = (val>>(6-(cdi&7)))&3; - img[row*bpl + (col>>2)] |= (val << (6-((col&3)<<1))); - } - if(bpp==4) { - var val = data[cdi>>3]; val = (val>>(4-(cdi&7)))&15; - img[row*bpl + (col>>1)] |= (val << (4-((col&1)<<2))); - } - if(bpp>=8) { - var ii = row*bpl+col*cbpp; - for(var j=0; j>3)+j]; - } - cdi+=bpp; col+=ci; - } - y++; row += ri; - } - if(sw*sh!=0) di += sh * (1 + bpll); - pass = pass + 1; - } - return img; - } - - function _getBPP(out) { - var noc = [1,null,3,1,2,null,4][out.ctype]; - return noc * out.depth; - } - - function _filterZero(data, out, off, w, h) - { - var bpp = _getBPP(out), bpl = Math.ceil(w*bpp/8); - bpp = Math.ceil(bpp/8); - - var i,di, type=data[off], x=0; - - if(type>1) data[off]=[0,0,1][type-2]; - if(type==3) for(x=bpp; x>>1) )&255; - - for(var y=0; y>>1)); - for(; x>>1) ); } - else { for(; x=0 && yoff>=0) { si = (y*sw+x)<<2; ti = (( yoff+y)*tw+xoff+x)<<2; } - else { si = ((-yoff+y)*sw-xoff+x)<<2; ti = (y*tw+x)<<2; } - - if (mode==0) { tb[ti] = sb[si]; tb[ti+1] = sb[si+1]; tb[ti+2] = sb[si+2]; tb[ti+3] = sb[si+3]; } - else if(mode==1) { - var fa = sb[si+3]*(1/255), fr=sb[si]*fa, fg=sb[si+1]*fa, fb=sb[si+2]*fa; - var ba = tb[ti+3]*(1/255), br=tb[ti]*ba, bg=tb[ti+1]*ba, bb=tb[ti+2]*ba; - - var ifa=1-fa, oa = fa+ba*ifa, ioa = (oa==0?0:1/oa); - tb[ti+3] = 255*oa; - tb[ti+0] = (fr+br*ifa)*ioa; - tb[ti+1] = (fg+bg*ifa)*ioa; - tb[ti+2] = (fb+bb*ifa)*ioa; - } - else if(mode==2){ // copy only differences, otherwise zero - var fa = sb[si+3], fr=sb[si], fg=sb[si+1], fb=sb[si+2]; - var ba = tb[ti+3], br=tb[ti], bg=tb[ti+1], bb=tb[ti+2]; - if(fa==ba && fr==br && fg==bg && fb==bb) { tb[ti]=0; tb[ti+1]=0; tb[ti+2]=0; tb[ti+3]=0; } - else { tb[ti]=fr; tb[ti+1]=fg; tb[ti+2]=fb; tb[ti+3]=fa; } - } - else if(mode==3){ // check if can be blended - var fa = sb[si+3], fr=sb[si], fg=sb[si+1], fb=sb[si+2]; - var ba = tb[ti+3], br=tb[ti], bg=tb[ti+1], bb=tb[ti+2]; - if(fa==ba && fr==br && fg==bg && fb==bb) continue; - //if(fa!=255 && ba!=0) return false; - if(fa<220 && ba>20) return false; - } - } - return true; - } - - return { - decode:decode, - toRGBA8:toRGBA8, - _paeth:_paeth, - _copyTile:_copyTile, - _bin:_bin - }; - -})(); - - - - - - - - - -(function() { - var _copyTile = UPNG._copyTile, _bin=UPNG._bin, paeth = UPNG._paeth; - var crcLib = { - table : ( function() { - var tab = new Uint32Array(256); - for (var n=0; n<256; n++) { - var c = n; - for (var k=0; k<8; k++) { - if (c & 1) c = 0xedb88320 ^ (c >>> 1); - else c = c >>> 1; - } - tab[n] = c; } - return tab; })(), - update : function(c, buf, off, len) { - for (var i=0; i>> 8); - return c; - }, - crc : function(b,o,l) { return crcLib.update(0xffffffff,b,o,l) ^ 0xffffffff; } - } - - - function addErr(er, tg, ti, f) { - tg[ti]+=(er[0]*f)>>4; tg[ti+1]+=(er[1]*f)>>4; tg[ti+2]+=(er[2]*f)>>4; tg[ti+3]+=(er[3]*f)>>4; - } - function N(x) { return Math.max(0, Math.min(255, x)); } - function D(a,b) { var dr=a[0]-b[0], dg=a[1]-b[1], db=a[2]-b[2], da=a[3]-b[3]; return (dr*dr + dg*dg + db*db + da*da); } - - // MTD: 0: None, 1: floyd-steinberg, 2: Bayer - function dither(sb, w, h, plte, tb, oind, MTD) { - if(MTD==null) MTD=1; - - var pc=plte.length, nplt = [], rads=[]; - for(var i=0; i>>0)&255), ((c>>>8)&255), ((c>>>16)&255), ((c>>>24)&255)]); - } - for(var i=0; i>2] = ni; tb32[i>>2] = plte[ni]; - } - } - } - - - function encode(bufs, w, h, ps, dels, tabs, forbidPlte) - { - if(ps==null) ps=0; - if(forbidPlte==null) forbidPlte = false; - - var nimg = compress(bufs, w, h, ps, [false, false, false, 0, forbidPlte,false]); - compressPNG(nimg, -1); - - return _main(nimg, w, h, dels, tabs); - } - - function encodeLL(bufs, w, h, cc, ac, depth, dels, tabs) { - var nimg = { ctype: 0 + (cc==1 ? 0 : 2) + (ac==0 ? 0 : 4), depth: depth, frames: [] }; - - var time = Date.now(); - var bipp = (cc+ac)*depth, bipl = bipp * w; - for(var i=0; i1, pltAlpha = false; - - var cicc; - - var leng = 8 + (16+5+4) /*+ (9+4)*/ + (anim ? 20 : 0); - if(tabs["sRGB"]!=null) leng += 8+1+4; - if(tabs["pHYs"]!=null) leng += 8+9+4; - if(tabs["iCCP"]!=null) { cicc = pako.deflate(tabs["iCCP"]); leng += 8 + 11 + 2 + cicc.length + 4; } - if(nimg.ctype==3) { - var dl = nimg.plte.length; - for(var i=0; i>>24)!=255) pltAlpha = true; - leng += (8 + dl*3 + 4) + (pltAlpha ? (8 + dl*1 + 4) : 0); - } - for(var j=0; j>>8)&255, b=(c>>>16)&255; - data[offset+ti+0]=r; data[offset+ti+1]=g; data[offset+ti+2]=b; - } - offset+=dl*3; - wUi(data,offset,crc(data,offset-dl*3-4,dl*3+4)); offset+=4; // crc - - if(pltAlpha) { - wUi(data,offset, dl); offset+=4; - wAs(data,offset,"tRNS"); offset+=4; - for(var i=0; i>>24)&255; - offset+=dl; - wUi(data,offset,crc(data,offset-dl-4,dl+4)); offset+=4; // crc - } - } - - var fi = 0; - for(var j=0; j>2, bln>>2); inds.push(ind); - var bb = new Uint8Array(qres.abuf,cof,bln); - - //console.log(frm.img, frm.width, frm.height); - //var time = Date.now(); - if(dith) dither(frm.img, frm.rect.width, frm.rect.height, plte, bb, ind); - //console.log(Date.now()-time); - frm.img.set(bb); cof+=bln; - } - - //console.log("quantize", Date.now()-time); time = Date.now(); - } - else { - // what if ps==0, but there are <=256 colors? we still need to detect, if the palette could be used - for(var j=0; jnw && c==img32[i-nw]) ind[i]=ind[i-nw]; - else { - var cmc = cmap[c]; - if(cmc==null) { cmap[c]=cmc=plte.length; plte.push(c); if(plte.length>=300) break; } - ind[i]=cmc; - } - } - } - //console.log("make palette", Date.now()-time); time = Date.now(); - } - - var cc=plte.length; //console.log("colors:",cc); - if(cc<=256 && forbidPlte==false) { - if(cc<= 2) depth=1; else if(cc<= 4) depth=2; else if(cc<=16) depth=4; else depth=8; - depth = Math.max(depth, minBits); - } - - for(var j=0; j>1)] |= (inj[ii+x]<<(4-(x&1)*4)); - else if(depth==2) for(var x=0; x>2)] |= (inj[ii+x]<<(6-(x&3)*2)); - else if(depth==1) for(var x=0; x>3)] |= (inj[ii+x]<<(7-(x&7)*1)); - } - cimg=nimg; ctype=3; bpp=1; - } - else if(gotAlpha==false && frms.length==1) { // some next "reduced" frames may contain alpha for blending - var nimg = new Uint8Array(nw*nh*3), area=nw*nh; - for(var i=0; i palette indices", Date.now()-time); time = Date.now(); - - return {ctype:ctype, depth:depth, plte:plte, frames:frms }; - } - function framize(bufs,w,h,alwaysBlend,evenCrd,forbidPrev) { - /* DISPOSE - - 0 : no change - - 1 : clear to transparent - - 2 : retstore to content before rendering (previous frame disposed) - BLEND - - 0 : replace - - 1 : blend - */ - var frms = []; - for(var j=0; jmax) max=x; - if(ymay) may=y; - } - } - if(max==-1) mix=miy=max=may=0; - if(evenCrd) { if((mix&1)==1)mix--; if((miy&1)==1)miy--; } - var sarea = (max-mix+1)*(may-miy+1); - if(sareamax) max=cx; - if(cymay) may=cy; - } - } - if(max==-1) mix=miy=max=may=0; - if(evenCrd) { if((mix&1)==1)mix--; if((miy&1)==1)miy--; } - r = {x:mix, y:miy, width:max-mix+1, height:may-miy+1}; - - var fr = frms[i]; fr.rect = r; fr.blend = 1; fr.img = new Uint8Array(r.width*r.height*4); - if(frms[i-1].dispose==0) { - _copyTile(pimg,w,h, fr.img,r.width,r.height, -r.x,-r.y, 0); - _prepareDiff(cimg,w,h,fr.img,r); - } - else - _copyTile(cimg,w,h, fr.img,r.width,r.height, -r.x,-r.y, 0); - } - function _prepareDiff(cimg, w,h, nimg, rec) { - _copyTile(cimg,w,h, nimg,rec.width,rec.height, -rec.x,-rec.y, 2); - } - - function _filterZero(img,h,bpp,bpl,data, filter, levelZero) - { - var fls = [], ftry=[0,1,2,3,4]; - if (filter!=-1) ftry=[filter]; - else if(h*bpl>500000 || bpp==1) ftry=[0]; - var opts; if(levelZero) opts={level:0}; - - - var CMPR = (data.length>10e6 && window.UZIP!=null) ? window.UZIP : pako; - - var time = Date.now(); - for(var i=0; i>1) +256)&255; - if(type==4) for(var x=bpp; x>1))&255; - for(var x=bpp; x>1))&255; } - if(type==4) { for(var x= 0; x>2), nd; - if(K<=60) { findNearest(sb,inds,clr8); remap(inds,tb32,cl32); } - else if(sb.length<32e6) // precise, but slow :( - //for(var j=0; j<4; j++) - for(var i=0; i>2] = nd.ind; tb32[i>>2] = nd.est.rgba; - } - else - for(var i=0; i>2] = nd.ind; tb32[i>>2] = nd.est.rgba; - } - - //console.log(Date.now()-time, "nearest found"); time = Date.now(); - - if(doKmeans || sb.length*K<10*4e6) { - var le = 1e9; - for(var i=0; i<10; i++) { - var ce = kmeans(sb, inds, clr8); //console.log(i,ce); - if(ce/le>0.997) break; le=ce; - } - for(var i=0; i>>2; - var sums = new Uint32Array(K*4), cnts = new Uint32Array(K); - - for(var i=0; i>>2], qi=ind*4; - cnts[ind]++; - sums[qi ]+=sb[i ]; sums[qi+1]+=sb[i+1]; - sums[qi+2]+=sb[i+2]; sums[qi+3]+=sb[i+3]; - } - for(var i=0; i>>2]); - } - - function findNearest(sb,inds,plte) { - var terr = 0, K=plte.length>>>2; - - var nd = []; // squared half-distance to the nearest color - for(var i=0; i>>2], qi=ti*4, dr=r-plte[qi], dg=g-plte[qi+1], db=b-plte[qi+2], da=a-plte[qi+3], te = dr*dr+dg*dg+db*db+da*da; - if(te>nd[ti]) for(var j=0; j>>2]=ti; - terr+=te; - } - return terr/(sb.length>>>2); - } - - function getKDtree(nimg, ps, err) { - if(err==null) err = 0.0001; - var nimg32 = new Uint32Array(nimg.buffer); - - var root = {i0:0, i1:nimg.length, bst:null, est:null, tdst:0, left:null, right:null }; // basic statistic, extra statistic - root.bst = stats( nimg,root.i0, root.i1 ); root.est = estats( root.bst ); - var leafs = [root]; - - while(leafs.length maxL) { maxL=leafs[i].est.L; mi=i; } - if(maxL=s0 || node.i1<=s0); - //console.log(maxL, leafs.length, mi); - if(s0wrong) { node.est.L=0; continue; } - - - var ln = {i0:node.i0, i1:s0, bst:null, est:null, tdst:0, left:null, right:null }; ln.bst = stats( nimg, ln.i0, ln.i1 ); - ln.est = estats( ln.bst ); - var rn = {i0:s0, i1:node.i1, bst:null, est:null, tdst:0, left:null, right:null }; rn.bst = {R:[], m:[], N:node.bst.N-ln.bst.N}; - for(var i=0; i<16; i++) rn.bst.R[i] = node.bst.R[i]-ln.bst.R[i]; - for(var i=0; i< 4; i++) rn.bst.m[i] = node.bst.m[i]-ln.bst.m[i]; - rn.est = estats( rn.bst ); - - node.left = ln; node.right = rn; - leafs[mi]=ln; leafs.push(rn); - } - leafs.sort(function(a,b) { return b.bst.N-a.bst.N; }); - for(var i=0; i0) { node0=nd.right; node1=nd.left; } - - var ln = getNearest(node0, r,g,b,a); - if(ln.tdst<=pd*pd) return ln; - var rn = getNearest(node1, r,g,b,a); - return rn.tdst eMq) i1-=4; - if(i0>=i1) break; - - var t = nimg32[i0>>2]; nimg32[i0>>2] = nimg32[i1>>2]; nimg32[i1>>2]=t; - - i0+=4; i1-=4; - } - while(vecDot(nimg, i0, e)>eMq) i0-=4; - return i0+4; - } - function vecDot(nimg, i, e) - { - return nimg[i]*e[0] + nimg[i+1]*e[1] + nimg[i+2]*e[2] + nimg[i+3]*e[3]; - } - function stats(nimg, i0, i1){ - var R = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; - var m = [0,0,0,0]; - var N = (i1-i0)>>2; - for(var i=i0; i>>0) }; - } - var M4 = { - multVec : function(m,v) { - return [ - m[ 0]*v[0] + m[ 1]*v[1] + m[ 2]*v[2] + m[ 3]*v[3], - m[ 4]*v[0] + m[ 5]*v[1] + m[ 6]*v[2] + m[ 7]*v[3], - m[ 8]*v[0] + m[ 9]*v[1] + m[10]*v[2] + m[11]*v[3], - m[12]*v[0] + m[13]*v[1] + m[14]*v[2] + m[15]*v[3] - ]; - }, - dot : function(x,y) { return x[0]*y[0]+x[1]*y[1]+x[2]*y[2]+x[3]*y[3]; }, - sml : function(a,y) { return [a*y[0],a*y[1],a*y[2],a*y[3]]; } - } - - function concatRGBA(bufs) { - var tlen = 0; - for(var i=0; i {
  • 链接:${image.url}
  • 上传时间:${image.date && formatDate(image.date)}
  • 大小:${image.size ? formatSize(image.size) : '未知'}
  • + ${image.originSize ? `
  • 压缩前大小:${formatSize(image.originSize)}
  • ` : ''} ` }) @@ -56,7 +57,8 @@ const showImage = computed(() => { - {{ formatSize(image.size) }} + {{ formatSize(image.size) + }} 🔍 url markdown @@ -85,7 +87,7 @@ ul.el-upload-list { display: flex; justify-content: space-between; - .left{ + .left { flex: 1; } @@ -127,4 +129,5 @@ ul.el-upload-list { li { word-break: break-all; } -} \ No newline at end of file +} + \ No newline at end of file diff --git a/packages/client/src/components/ImageUpload.vue b/packages/client/src/components/ImageUpload.vue index eb0aae8..1e20a42 100644 --- a/packages/client/src/components/ImageUpload.vue +++ b/packages/client/src/components/ImageUpload.vue @@ -2,12 +2,12 @@ import { UploadFilled } from '@element-plus/icons-vue' import { computed, ref, watch } from 'vue'; import { ElMessage, type UploadInstance, type UploadProps, type UploadUserFile } from 'element-plus' -import { compressImage, uploadFile } from '../utils/qiniu' +import { uploadFile } from '../utils/qiniu' import { useFocus } from '@vueuse/core'; import { useConfigStore, useImageStore } from '@/store' import { storeToRefs } from 'pinia'; -import { formatSize } from '@/utils/stringUtil'; import { useUploadConfig } from '@/composables'; +import { compressImage } from '@/utils/file'; const imageStore = useImageStore() const configStore = useConfigStore() @@ -35,17 +35,14 @@ watch(files, async () => { if (!file.raw) { continue } + let fileRaw = file.raw if (cacheConfig.value.compressImage) { - // 采取自动压缩策略(TODO: 未来开放自定义调整) - compressImage(file.raw, { - noCompressIfLarger: true, - quality: 0.5 - }).then(v => { - console.log('origin', formatSize(file.raw!.size), 'result', formatSize(v.dist.size)); - }) + // TODO: 未来开放自定义调整 + // 采取自动压缩策略 + fileRaw = await compressImage(file.raw) as any } - uploadFile(file.raw, qiniu.value, { + uploadFile(fileRaw, qiniu.value, { process(percent) { file.percentage = percent if (percent === 100) { @@ -61,8 +58,9 @@ watch(files, async () => { imageStore.addImage({ url: v, name: file.name || 'image', - file: file.raw, - size: file.raw?.size || 0, + file: fileRaw, + size: fileRaw?.size || 0, + originSize: fileRaw === file.raw? 0 : file.raw?.size, }) }).catch(err => { ElMessage.error(err) diff --git a/packages/client/src/components/UploadTool.vue b/packages/client/src/components/UploadTool.vue index 8d61f84..3eb1b02 100644 --- a/packages/client/src/components/UploadTool.vue +++ b/packages/client/src/components/UploadTool.vue @@ -19,7 +19,7 @@ watch(() => success.value.length, () => { diff --git a/packages/client/src/shims-vue.d.ts b/packages/client/src/shims-vue.d.ts index 12401b6..b021f5e 100644 --- a/packages/client/src/shims-vue.d.ts +++ b/packages/client/src/shims-vue.d.ts @@ -1,4 +1,4 @@ // 第三方库的类型定义 -interface Window { - UPNG: any -} +// interface Window { +// UPNG: any +// } diff --git a/packages/client/src/store/modules/imageStore.ts b/packages/client/src/store/modules/imageStore.ts index 030a077..7195f09 100644 --- a/packages/client/src/store/modules/imageStore.ts +++ b/packages/client/src/store/modules/imageStore.ts @@ -1,6 +1,13 @@ import { defineStore } from 'pinia' -export interface IImage { url: string, name: string, file?: File, date?: number, size: number } +export interface IImage { + url: string + name: string + file?: File + date?: number + size: number + originSize?: number +} const imgStore = defineStore('imgStore', { state: () => ({ diff --git a/packages/client/src/utils/file.ts b/packages/client/src/utils/file.ts new file mode 100644 index 0000000..f1ba6f5 --- /dev/null +++ b/packages/client/src/utils/file.ts @@ -0,0 +1,94 @@ +// @ts-expect-error +import UPNG from 'upng-js' +interface CompressOptions { + /** + * 压缩质量(0-100) + * @default 80 + */ + quality?: number + /** + * 压缩后更大是否使用原图 + * @default true + */ + noCompressIfLarger?: boolean + /** + * 压缩后的新宽度 + * @default 原尺寸 + */ + width?: number + /** + * 压缩后新高度 + * @default 原尺寸 + */ + height?: number +} +async function compressImage(file: File, ops: CompressOptions = {}) { + const { width, height, quality = 80, noCompressIfLarger } = ops + const isPng = await isPNG(file) + let newFile: File | null = null + if (isPng) { + const arrayBuffer = await getBlobArrayBuffer(file) + const decoded = UPNG.decode(arrayBuffer) + const rgba8 = UPNG.toRGBA8(decoded) + const compressed = UPNG.encode(rgba8, width || decoded.width, height || decoded.height, convertQualityToBit(quality)) + newFile = new File([compressed], file.name, { type: 'image/png' }) + } + + if (!newFile) { + return file + } + + if (!noCompressIfLarger) { + return newFile + } + + return file.size > newFile.size ? newFile : file +} + +function getBlobArrayBuffer(file: Blob): Promise { + return file.arrayBuffer() +} + +async function isPNG(file: File) { + const arraybuffer = await getBlobArrayBuffer(file.slice(0, 8)) + return signatureEqual(arraybuffer, [137, 80, 78, 71, 13, 10, 26, 10]) +} + +function signatureEqual(source: ArrayBuffer, signature: number[]) { + const array = new Uint8Array(source) + for (let i = 0; i < signature.length; i++) { + if (array[i] !== signature[i]) { + return false + } + } + return true +} + +function getImageDimensions(file: File): Promise<{ width: number, height: number }> { + return new Promise ((resolve) => { + const img = new Image() + img.onload = function () { + resolve({ width: img.width, height: img.height }) + } + img.onerror = function () { + resolve({ width: 0, height: 0 }) + } + img.src = URL.createObjectURL(file) + }) +} + +function convertQualityToBit(quality: number): number { + let bit = 0 + if (quality > 100 || quality < 0) { + bit = 0 + } + else { + bit = !quality ? 0 : quality * 256 * 0.01 + } + return bit +} + +export { + compressImage, + getImageDimensions, +} diff --git a/packages/client/src/utils/qiniu.ts b/packages/client/src/utils/qiniu.ts index d9628c0..4f320ce 100644 --- a/packages/client/src/utils/qiniu.ts +++ b/packages/client/src/utils/qiniu.ts @@ -49,34 +49,7 @@ async function generateNewFileKey(file: File, prefix = 'mdImg', scope = 'sugar') return `${prefix}/${scope}/${md5}` } -interface CompressOptions { - /** - * 压缩质量 - */ - quality?: number - /** - * 压缩后更大是否使用原图 - */ - noCompressIfLarger?: boolean - /** - * 压缩后的新宽度 - */ - width?: number - /** - * 压缩后新高度 - */ - height?: number -} - -async function compressImage(file: File, ops: CompressOptions) { - const { type, size } = file - // 根据类型选择不同的压缩工具 - if (type === 'image/png') { - // window.UPNG - } -} export { uploadFile, generateNewFileKey, - compressImage, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6270787..0827788 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,9 @@ importers: spark-md5: specifier: ^3.0.2 version: 3.0.2 + upng-js: + specifier: ^2.1.0 + version: 2.1.0 vue: specifier: ^3.4.15 version: 3.4.15(typescript@5.3.3) @@ -4431,6 +4434,10 @@ packages: netmask: 2.0.2 dev: false + /pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: false + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -5541,6 +5548,12 @@ packages: picocolors: 1.0.0 dev: true + /upng-js@2.1.0: + resolution: {integrity: sha512-d3xzZzpMP64YkjP5pr8gNyvBt7dLk/uGI67EctzDuVp4lCZyVMo0aJO6l/VDlgbInJYDY6cnClLoBp29eKWI6g==} + dependencies: + pako: 1.0.11 + dev: false + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: