From 61c30d51210762240ced6b2ae1fc9ff1709edb07 Mon Sep 17 00:00:00 2001 From: dengtongyu Date: Fri, 5 Mar 2021 16:01:24 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../浏览器端crc32文件计算以及分片上传思路.md | 133 ++++++++++++++++++ source/images/AAAABSGsf8u86EvIjRhj.jpg | Bin 0 -> 18891 bytes ...-0dff7a84fe57e0d911f6efd2ba445db5_720w.jpg | Bin ...1497fa6a5de2596909dc2af7a6233e37_1440w.jpg | Bin .../v2-1ff0597480e2ef8999a2bc86a69f6a3e_r.jpg | Bin ...498e45cff82e401ae43378031b8fc740_1440w.jpg | Bin ...-498e45cff82e401ae43378031b8fc740_256w.jpg | Bin .../v2-5d580945ed25ce42f2fa5bea4cb0d278_r.jpg | Bin .../v2-71c2d3e28a607d4732580d5fa8fcbac7_r.jpg | Bin ...-8336cecd23d2a4134033791a8e5211fa_720w.jpg | Bin ...-94c8508fb5f0acc59dd3616512a6cf47_720w.jpg | Bin ...v2-be9ee0a7eef1372e6ff9891436c61389_hd.gif | Bin ...-d1ccc3ef1cec99e3ae06d300c5ca0f7c_720w.jpg | Bin ...-e6df90886acf0bfc934f2902cec54143_720w.jpg | Bin ...-f119c1dab2d95d62b0808f3b334dd8a1_720w.jpg | Bin themes/butterfly/_config.yml | 22 +-- 16 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 source/_posts/浏览器端crc32文件计算以及分片上传思路.md create mode 100644 source/images/AAAABSGsf8u86EvIjRhj.jpg rename source/images/{ => config}/v2-0dff7a84fe57e0d911f6efd2ba445db5_720w.jpg (100%) rename source/images/{ => config}/v2-1497fa6a5de2596909dc2af7a6233e37_1440w.jpg (100%) rename source/images/{ => config}/v2-1ff0597480e2ef8999a2bc86a69f6a3e_r.jpg (100%) rename source/images/{ => config}/v2-498e45cff82e401ae43378031b8fc740_1440w.jpg (100%) rename source/images/{ => config}/v2-498e45cff82e401ae43378031b8fc740_256w.jpg (100%) rename source/images/{ => config}/v2-5d580945ed25ce42f2fa5bea4cb0d278_r.jpg (100%) rename source/images/{ => config}/v2-71c2d3e28a607d4732580d5fa8fcbac7_r.jpg (100%) rename source/images/{ => config}/v2-8336cecd23d2a4134033791a8e5211fa_720w.jpg (100%) rename source/images/{ => config}/v2-94c8508fb5f0acc59dd3616512a6cf47_720w.jpg (100%) rename source/images/{ => config}/v2-be9ee0a7eef1372e6ff9891436c61389_hd.gif (100%) rename source/images/{ => config}/v2-d1ccc3ef1cec99e3ae06d300c5ca0f7c_720w.jpg (100%) rename source/images/{ => config}/v2-e6df90886acf0bfc934f2902cec54143_720w.jpg (100%) rename source/images/{ => config}/v2-f119c1dab2d95d62b0808f3b334dd8a1_720w.jpg (100%) diff --git a/source/_posts/浏览器端crc32文件计算以及分片上传思路.md b/source/_posts/浏览器端crc32文件计算以及分片上传思路.md new file mode 100644 index 0000000..c903ae2 --- /dev/null +++ b/source/_posts/浏览器端crc32文件计算以及分片上传思路.md @@ -0,0 +1,133 @@ +--- +title: 浏览器端crc32文件计算以及分片上传思路 +id: '362' +cover: '/images/AAAABSGsf8u86EvIjRhj.jpg' +categories: + - 分享 +tags: + - JavaScript + - html + - crc32 + - 浏览器 + - 分片上传 +date: 2020-12-27 15:21:00 +--- + +大文件上传我们知道需要做MD5计算以及文件分片,由于最近业务业务上的调整,需要把MD5换成crc32,,crc32可以校验文件的完整性,对于业务处理比较方便。这里记录一下解决思路吧。 + +# 1. 文件分片 + +考虑到文件可能很大,出于性能的考虑,优先把文件分片,这里我设置默认5M大小一个分片: + +``` js +const createFileChunk = (file, size = 5 * 1024 * 1024) => { + let chunks = []; + let cur = 0; + while (cur < file.size) { + chunks.push(file.slice(cur, cur + size)); + cur += size; + } + return chunks; +}; +``` + +# 2. 文件crc32计算 + +首先找一个crc32的库,这里我选了sheetjs的[crc32](https://github.com/SheetJS/js-crc32)库。 +为了提高性能,这种纯计算的功能使用web workers另起一个线程处理。 + +``` js +// crc32_worker.js + +// 引入crc32.js库,importScripts只支持url引入 +self.importScripts("https://cdn.bootcdn.net/ajax/libs/crc-32/1.2.0/crc32.js"); + +// 生成文件 hash +self.onmessage = e => { + const { + fileChunks + } = e.data; + let crc32 = null; + let percentage = 0; + let count = 0; + const loadNext = index => { + const reader = new FileReader(); + reader.readAsArrayBuffer(fileChunks[index]); + reader.onload = e => { + count++; + const blob = new Uint8Array(e.target.result); + if (index === 0) { + crc32 = self.CRC32.buf(blob); + } else { + crc32 = self.CRC32.buf(blob, crc32); + } + + if (count === fileChunks.length) { + self.postMessage({ + percentage: 100, + hash: crc32 + }); + self.close(); + } else { + percentage += 100 / fileChunks.length; + self.postMessage({ + percentage + }); + loadNext(count); + } + }; + }; + loadNext(0); +}; +``` + +web workers调用以及封装: + +``` js +// 计算文件 crc32(web-worker)参数为上面分片后返回的chunks +const createFileCRC32InWorker = fileChunks => { + return new Promise(resolve => { + // 引入上面的worker文件 + const worker = new Worker("./crc32_worker.js"); + worker.postMessage({ + fileChunks + }); + worker.onmessage = e => { + // 这边加了进度条 这里的进度条,看需要显示 + const { + percentage, + hash + } = e.data; + hash && resolve(hash); + }; + }); +}; +``` + +# 3. 上传进度本地缓存 + +计算出的crc32值用来作为判断文件的key,可在localStorage缓存分片上传的进度。 + +``` js +const uploadCache = { + 1259060791: { + position: [0, 0, 0] + } +} +``` + +如上代码, ` 1259060791 ` 为文件的crc32值,把它作为key, ` position ` 为上传的进度,比如一个15M的文件,会分成3片(默认设置了每片5M),然后根据自己的上传业务,上传完成的分片标记为 ` 1 ` 。 + +``` js +const uploadCache = { + 1259060791: { + position: [1, 0, 0] // 第一片上传完成后标记为1 + } +} +``` + +值得注意的是,通常分片上传都会多个分片并行上传,标记为 ` 1 ` 需要留意 ` position ` 的下标跟分片的下标是否一致。 + +# 4. 浏览器刷新后获取文件的问题 + +由于浏览器的安全机制,刷新后无法自动获取到原来的文件,所以需要通过提示去引导用户重现选择该文件,然后才可以断点续传。 diff --git a/source/images/AAAABSGsf8u86EvIjRhj.jpg b/source/images/AAAABSGsf8u86EvIjRhj.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2efd57e55cedcddf9f67b6c291b6b38d01f464a5 GIT binary patch literal 18891 zcmb5URZtvE6E3{CyTh`$OK_I}K^J!qyg+bwCpZL`#TR#XO9;M5fZ!h7-JSgTs!r8k z=koMa%}vYGbJNrIcj@mY09Q#)Q4RnH2LQnRJAl8d02u%h0wN+J0@A+=2?+@q1rrtJ zAK_qNpkd1jdqKoAw^e@5W`siGjG zV56d9g9z~mLI2P6w-^1OxOXqXey^0z^5l|7pSfUnu_vS(*KcxgU3Ta#HT^xmLkyL zF?TH_q)l$TME=@0y>%r`^mhe-iSW-L9s(Xf63`X--$3eEM`4F1CYnB+!E5o@CGT)q zWnOB5y7A!%Tq>VGku#+V{ncrTLN5BA)Sw50&{#~)?by|m@o63pk5}eowHi?mLgogA z+8T@5;CV{RyB_W1SpIwWUx3H1H|ua9KhE2N?`;e?ug_ZFpHvPFktn}MzaamPEM#Zi zs=|}V5+8wXy!W)Gbbjb@5G?o?kd+Vo`GIE5i-TA79{2MNGf|hFPm_h$CV`usQX;R1 z%PO&2Wl-{$`PoY9`(|KG>yXla?KcP?XLsMuD52bgGEi!iIsE>XUvy z0F^pK0pX*k6#h5d2E51;zO;rtqjbCmN9Ie-=A=003(9DYd&QOfEj%p)_+gjGxvhSN z|1t+1Wy8PIL@-d}g>&IY%b**DsKQf7>*DqcB1Nx6&b8ouXIHS1Bgfk!H|ifLrU7jy zGI!`|noGCS{lVwL?Ke_a-`|YG&F)8C35{yP-QrJ)!-xDXoFgzF0`01+*Ob%wXP*P* z_qfvp>9;dATvgN$0EI()HI@?qMX;`h@hREnI^8RJrl#?v17r(v?RcVdiHO5Na~S9p(K5gKYYUMYidQtl3z?NRWQ~hHeMFfS z`>kdvEyq+0;aAuHGjRCL_Ew96UY6Zgep(J@iB-u^^-GE%3JI^&&d!5a*OA$gb#TUy zab0P%q#1ieh?zw-VxUPXM#V~|F`VFCQ5^48_*V0dmRPQzR1-_|2fi})ty;iGE5xIi ze`2&GjQsE zT3o0{<$_URb5|;}o0yE~VniZMiv|kU%QW7gF|rXMy7V4iP2CVxbW-N)M3yDAJ|#dw zItB11GOlD7fPiKo^~>L{p=9@&SqV4agE7JiUYg!0r`y(Ls?1kOs2J2=K$9lP!eB5) zS`%(dhH8ba4c`>0VM#k8ogPwCG^WF+r9HXCQWP-LU98U&{1+f`A}5zUyKpaCQ{&C3 z#zyZ!^qbe*oh%XUT!&J+Y#-Rq*{w(G=cGF~vc*Ke&qA?Q&=DSnw8; z=c@8XoSAQv4!=7NdZPvZ`cGWC5T6h;Rw5SuYCZ`XQ?5pKUa1^4kC*$DS}OIFr|o-fcs;+gLbe}D_G)<3PEV`LTu)N!h?95n z`|11<)P=uj!uR1x(Z}hj=}<2t(Y@g47ZtX4Fn`5fhw({#I>mu?byGLac1ieXYiI zQ$wDh0m>;i6wM~@3j#;tv5jy?ERQK_NXOuQ6x`g`xa3D2SP4vU01z*z`?DMTAiCNr zURM}35se=xT~S-npb8H6SG+Y2n%#D6Q!1Czk`ZGl$Hx@?mH2Kmf_*N6Fz-Pj+aFn& zJcpG!J0i$`DvS*p>%3ho3(RJRu*fpLNQy5W$NU9g*(YraLbW0KG)eHIFxHtl2lw1| z2d7&$8L*YLSGu8Xp_qaB7>137ib=0HuYV#udZw7SkM`>H0LPBfA ziuSzx%9u+np=*kyG<=TQG)wN%b3Fn-zI*79uvz?B6k$pVB=&^5dF*|(b{5FCsj!Bo zp^Oca2#BwGTjW|dwG;%bd{g>ye|W^q(S%Q15SteSY~;Ok6SC_BGvA7@chk!`E<>S0pIr7 zVk6X3>}+&N0fh{X=W_qfmMWrp{+9o@j>v(GqWX&^?9L7`xMb8l4&Lwleq==Kx*{NZ zbjg^86I8&yE`!*T9b@5XDiLL0a`Ka^7*_Gdfb|^nqE9G zo=bd;zHxv*MPXSB;PSObpa6*^l<+U#jUC=jkk9QKJ;q+VC#V_B+wnasj;l~Evn4*f z_3Zc0+ZVNIJ!!iLaKEV@ANZ%PyVa99d)zH%aaKX8 z{^q5P=+Soc&L=AD{sEDH0RvFr&Qjdhc~|hC>Xy}kY$hUJ%x$5B^Wbf7wT<*MPqM}x z*inCqg!dc&Z_n%>pSN$yR)U;XAA)JB>?fxPYqjHpXuo;je2XCtIdN2 z>I-U;2S<(%OI)Yisi&Um!DqW~i{Fl&y=3W5{o+|?<#3+@2hHij&*%L0_{QXDn9vpf zlPOlVK%`YAPD%|&LX|=Dkqy@%4lhVV!e>T6Vxb{rE+C99mJL5dOp|2}*Jmk=p+PLs zSC*pN1H=PxnXf}hl`SvhLAd=9si<@^sOH4fE}^9I;rfX2m^9(1aN%5dA&GXn+%$!3 zHvJTuVH#%td8%NgG(D&=EOfPrJOzw!6rl@Bh?uRBh*&3wEK=ogh=wGVvwhfxKwEm3 zPtF9T+=$17%9F3NFgDooB1Z%PhW+l5xz?0(dDgBK4U6-?s2Gf7!F$&m+_ur8do%?W zaYCuNP&Ee&I=8v|?g+NZ{Xi26%WvbkQV7OJ@<8F;-6VMzt{)PZLUYvbrhfr+_!G4$ z&Qwt*9d8o(Zuv%CJ9c%gCYDtJH&0mQc^AIk+4?-ZP*p=0_ro>LuUo?CnbCH36^C1E=7MoiiNOx ztvmXY^i62o(Gm)dYyCpSKVVNG8W*1g2NMD$bh4gyxE>@d=r`H%CF@@Y@Ob?NO!)J* z47e8r)`=Fg(K!OcII<>J#JQ(j{1S@08jHHLC6GtKIz3F-z(q@KuQ2QLTITxZPh0yf-&q69l9T&jD9pXaS=-?q zDfD^Oa$Oucq`ll`EDgsZ!ra{vk#NjN*(Q>hQSu69wZ#==mhr|Xk6$0jIoMubwYylW zc-yO3h%7jtoHa1>xzkAySl{D#rEem6X_p(ZFU*2CFowe*uUS)sZL%Neit_TrHRgy~E-ieWSM~_GROr%R2%z-u0e&1QX@c zWju#Xdh$0MMm!SQ22*zmLPg{ku;%r=mEIJGYI=XzT(dx51 zNT=~wmw}mu5B;zVO5xN;9IG`k{5egZLM*G{lsup*5+s2c-Zx*Uhr+4vMG{ZqbNFLk2g1@~u2LtY`{}c}1<@*b;TJv&GpQ<6c zy|P>7U;wv`8|dwF7i=b`T?ZZ5yhbdra(Lo(d?jOxTHui;E2c^g!w%?lEl zq0+67-Fn{~t#m@OXDHllMwC8Dy9gf)r#dL(?v~t+ZXQjTiYEJ&05n1z{!%0ngA^f( zJ8H*Kc~(A6Jf}YlR$qaxM1MW}23JD|Cq#~}WymA6B??!=07FdS>x}0_s3VSC1lv|e zIn-V6_UAcycCGUk`paZr9P3}(-LGhPg$!r@!NyQKO)~6iWk9N`>|Uk7-mGjD$cRYW zw`05(?vLawBona<=`+V<6FMVG^qR)_`a)aLs?(3=!P$Fn)DBxiKc!PWg&|Vw6aLoV zwUuqDl=##!1-V$JZXKwg(N(ZUHJ2D!yNPT`mxx@ZPl-PjRZ>}WX(~q@&}~>UbGcO! zRZBc_tDlc_=4G-jx|e1 z`^C(NN+@@}Cb z159p9Qxwe~2r^lwgZco&4he%1j;XzhD$4`Xc$7|zc|WauiErGOCuvE3lW|93qQfX? zRW!S^_|uIyk;+&m%GfQj{i;8Ur*5mnT*0#A+}{}Tq!AfKjERp@X)ET>_5tIZaG~4r zr}DgSKBWfz46nW0)Kzl6ll+#GCr3h*y5YK0{mm!YGoJu8c7Gs_ZimOY#9&09b@$>C zr3x6WSvzw1gmGn?YF|w)bfKl2LRDV)PGTGxjl&cr;WIvid(@sz`_H9Y;(q+>4egG1 zpUhXh&VtSR5&3qE=Ol~wc=>GOP(6&(Hv2S9xCUnzjS*z)v8yg8dn~E=!h{fo4N{o&EYI+lcRfkIl4H^ZCk-AJW~*tFd&% z;zTYT7rn}Wr4CW(!{X$X`{)k*{Ixw7xqWUYhuqiGF}Y22=Y*ZaWrtp_DgCm^fOM^; z+g%E4bZ=s>JT>7*trQv#eaL()g8RN|c$Q20*!mScdB)x^A(NOY5kcgV7pJ8!S>fWLKq z08@Z+^Gq!A=j~`Iu_o@O)~FakZ@T8Fde=v#ORp;FlXQe!We2=p!>PTToY=5a^o6Un zRKD#+FbL{nH~T`=Y7=sSlO$jX zqS9$AERG#BzCGUGD}N!bS3b%vG%4pu4@(xO*F<C~z0dWgqPdQfHDIXt!jPk)`ul=mi7m3Uz0W{jVG0hvF=bg)E}%VVRC~d2EJWE= z^qTNZTa@i1sKTXoZ*~#2h)0Aj0^U$~hb+L!<^k0}>l*PtxK9=+P44EXj;uxzGRPl3 ztrquZwSFmI(DkXGzqh5Wzp?p7L0?nqzvI5Pz6xg}7GkLCx_DkQgtq62WNFRZXPo6^ zs6qU~{wC1gqWzi5o^O#9NOM!1ZKxNige-`Bl^U10ZcED!C04^&rFY{KPOu$u5l7Xv z91#0VUc@zu_R4OuSPge1z<;!NaQ1dh~FGC?tm(11g z*Nw+6eCGhw>SQ1Fgze`Le{0W;BJ!jdCVF<`DyaJmBBTv8-97=QK8qu zrC?saC=F;vIHX(PbxmuHPM>kyd!><+JpO>}E|MiDB6ZCvw8#P}7Se-I`7*R!T%Wg6?kSWgyeC}`?6llF97Kq0fu`{Pd2{L7{z^95}^bc*8L((*LXU#>Q; zah~?sfJr%dpa)wI1qF_L!8o&h(}9&ql;IU)@vh31Vj4$0tXNTb?zfh8bt!S55API+|34uRRrU(m;TTj zu6_0>d4bFq5d+9_av7{92BQ$>QB|-NlCfI1F5WqNz`lMAU=1(o1Z`HP6AJg zqnC(b&7|T4eUC!TRU~-cIwISxr)SGRgF~Fp|1(?DR*Hr1l&PV-M80S0Wf`l`?VFXh zE1!;lo;=&j85UCUlG&|^=wn9L%%c9XQ$Y&J@QBnp2;-ym-=b4R=8vS#nwSm*dl_%M z-aE=g$@ax;CAK&X37h>&OYYXIj&>~jaVQ$7Afn>RN!g^*wjH63_7!`%m#POHmNG3x zAg`xJpYV#@5^w!~pBX7mIJi`2*89F9CK{)Col*=yKnO z=%nngFBPcoX-WcPw7<;mOtwuJxQ#M%lARiP+;4Ag4~N$5cKmM5T$@3$8Viv4KDTlE zi^MP)UlG13XzfnL2^9+Uo|E zP1U2c*-X#VvDz^ID-`69ukH3)m%-cusi(Ybe?JDyvHJ|8I_OyVR-{9P`Xq(#Oo{BbjO$ddehK)~v^qPBNPSI6$=rNq zPnYc~BY}trT9?Zf6Bm(@0Q9y%MC-(Pb(M)e<~9vPPCD+U{gUndx3=mnX23BHLZJ8GKZ zSms(+=$?}f&k2OT!_)rOPKC=W#3~fsD`KaS0NC4n(x2O(iEc@X46$jTWk<70M46-I z?yiam$txt?DePBd_#q9qMUjLpplCEBk2BWXwix}GwHu+2tg(enk;JP;6v+FC)1NY0 z>^(~@nVvX`rUI1<bX2nAx}e z=p}4Z!FbPzh)eOM4>}-7>)21ji%Nb@;!}mEX7mT$*r-7EYBS0IT!JTzY`=Ld#u`>-ZSVtKCiYJ80uJ`< zufdTh*hk@}v<9{u3#@j}7R(Tl0vO4N$#>w6L?*SzgXs}n+D2&Xaq#k+t?1U|m|fAS zV5Rx)*NZ5ie@NFG^7pz~{uaZ!A`+7v1-JgmfF2ykHt>}Ab@^D>xTt8nf}fCZafD^U z;soDZ*~;&xx)k``ZdukStXmP7bS?W3E}VNtWRri)ts zf1r2<#%^DcomNNP3hSGkcpZ~rrIBo7YYz^(P_9qP2-HCDLn4JT`=}G4(NUpcWTgWV z5w`s-eC;JWtL#O(cDQEW@Mqy4@@qkXD%LLsRb`|OO+vxzGykdvi1rM)kugWa^A7S@ ztK(dk;h_5Usz=e(q_xC7F8?o}T6}B0db1W@(Im^xl)+cbF+N{a{y#OLvwSO!@|p+# z+uGVW&3TEh2~RA06qx6y0-FInyYBZVY^I-k$RS`AjF`%5YVh8jWw3&U>-svfsDDxx z`DGlWp_AvL;<)-h(L#E8ygLgYlxw$O8d0p|)TNsA+7#O3>?an}-isWNh3cuF1)aoq z4in#Xo+E;IGJN%nB>?E#M zD~C-L11O21R!|gR)C7Bl_!@DtL_eR^9NtNqDIOd-WuupbRsw@G7@bE;7+c4DvowS~kSq zFf+ok=Z|5L>f|MqK$-9;R&x`ZOgzPF7yE;BB~QvgMc%5evZA^wxs~5vxo?sl7$UqP zKRUue*W=S?Tv=M?(8|r%vjH-;6G&MQ3|h!YzTLe_9oD&h7T7Q_dToI%0VT zDY=Y@&C-2!PZSF85DMYh1g=4Wx(APeDUjJJ`dROGKAmwH4&I%tpKQ=kI2SLUi?_wj zu{TOl+w+x<&zzEdL;9tV;JjQnvUa83eTDs9X9s15txmFGpGn@h%i>s3UD))5Z&je! z)hqAPNHH(5^XP%l`sgk|O?k_2K>Ja$xcW4MnFB)Mh~=pK!9h!au1&D}IZSFeXgu5J z1d9jVN&J<&^6)c-DL{+K7|MvaTES$<4Ta<&D;hhTP~2#y!*mz1Uh(;-h=WpE?ujZ z*tYiFHE##11KJ0Q1YxGSzl5_r8Tsk*abxTYX9x%~Bxwu;l{8_Xr+?pnc||BgCs~uG zs#cL$!GDHw{GrjI_~WRbq?y@OU%fwBI#*%G*C}v8hjf@yBmm-@r3h)`#Xb)z86&g( zQQO)k3hWuVv}@vo8k+8*c-fE${(OpSVsUD0Nn$O~x1i}aad#n_c~xa1hZugjl==+Uc8kSi z=>TPl>uDldBOA^ggKzvD`h!}-kU1U#u!PKz2qI~jF+`ogQc z9K~RbrPWS8mk>d&5a9d~crv`>Q#a#g8)K`#REsp53^{UQdekU6HrNUv)Kg$6tn*a9cI!d+@7M&E$n8;pir> zm;Zj~J0?ki8i3hG@f*+AQyTPKV8 z(#>;s1hc}Ev4=rJA0S^xA>LI$4qBf;vdtb{|1c=n}f?xMW z`@PN|eDwjBxp^WTM~_)8X@>BQg9G}Oj^`xq#4ead?@^h9MteN~=`bI)#e_JkL8XQt zY(xYX-X=sfP^Eatee@peRL_|0m+J;2(cA5-JNEH&(7tv*&G33l{`KA9ySDP-*=6fl zDT-^b6x-Kf`3g#jf9-u|vuU^00K!Crn7tHm?XaERP-|hbR_`11i(kr-F{QXZ6|KAW)e9;pbL3FC5(wJ_Yqv~L|(*F+j6lq!djWnSLgf!;YG~v>;w3{eE4XG ze#Yc0Gi`*0nSrPISiPnqKaNel?cZF6Q#JS~>(R1wp>rRT-&S|Udy*9y$s@ER z;3#&zEhmQ}tTDGcipc}vcWB3tE7v^r924kDNg&rRNga}M3;^j?oa?RdV`L~^0gKR*Xy6s zHSECI_^#5nCpu>@=q5;gPNwyQD1n zZ1tdjAo3?=cl(BuJR)bbK8~vHms!`svhr96vb-9ig*|CsYb_{*1B*yjEX zeAQsHW2Jef{s5{PY8u>{qxHCIuOwBGh0D)SpVMl2N}IGn?@}>hi=lACv8v2VYgJ)S z;gZsB%{EPuHo;p>4+xZGwykIM5AISArqup#(XVKrf99Hhvmw0`YeK`FR?#0;6*gD8 z4#y68ra!m)b^zKU$wZIZ-r4&4!5v*@1?I>6lI>p@>L;uxE_cqtMR-UtM>xL8V)h-> zsXxvXy)fST-n5KJ0MT@INXLp_^%hX+pnllgaSBR>j`=wfw#L_Rd42zIsh7&`TAf;~ zYwmK4=W#rbTvr9H+hnnA9cl`9*cfWFg}dhw#ajOhz~*W4o4GSEBfN99XZ7pSooj5R zxn< zWdk(WNCUYkXI10-z@mxTb8)VRnrT-)cvSn8W9lAZovO&@uyw6_#>T=)n5x9bTqDRYjp41R&>uY=ZE<*ICJzd67`&y6iEfC#%}3Qp;8z0uA~E zFF5)GF>E=1A|1}g?scboCf$XFU*@5Hv`5ejHXE;xU))fE@S-GaIguGnhBfWxp4HH* z%{L+$_o6R1B&j;QU){L|qFQT5o|<1Od$4SkVxyMN)6;?+YXW3d8R_6Z^ZCjaS8fta z72KQV2>6(VT1Odi2;FchETY&!~9z$irJ2f&4sAsCU0>!Z*E{_RMZ)%{dikRf-;DP1RR)z7ih5SENuaavV4JQsY( z7}GzZZW~IJQ2$ffd)YVg{PU;moMY=Emb3PMRVn0OrhBno_Jh)>JgN!-ghRz3GeYqI1UEJME6KogpO$k`)rt_nxNl&kAG$ zn+SvP>&!lP)FIrn=zjq!e@-)4r99>c(_3})ST&!jfR81Q2B7H}(*EfmzW`<_IL6rD zQcjLXK4foS=&-A|{soY*YjOd%l2Qr4jcaF95|tUB&n2;q7*u=BKDc`cQd^XNy1iPB zce*fs{|o3Wr|lFuHC?n9iOn!zqqWIP=N(m=I9vI|ofi*N_7b~Ez+pCY?BCk4LhAOP ze5AX+Z|8h6w%Wvb!g&yf(8AQvJSqDY<+L=k=1890qB`W@oK-itM|N`KpoI>3RqVfh zPMa{ZnL4>@^S4|?qzvs{(DIx;9$@(=;OKay57iBlVBRLUPt`NimW7R!#ZsQ9j+=cs z@bp6F(aJQXEVXt;+ry+&WMC!rQRI;C#9CGjXR@Om{2tDOr-1z0B36_)BI6=AhX69T zm2W5?$T@@n=SkvF|7fK)jBAig(`D!3*dYxp%kJQDLN9bc2zj0Q0|AHuXWH_pk?`M! z%;l2)(609G$w97fk``FMBtcLW&57_C>Z;w_1*Gw1O#;f{4#JmcI1)LYRS6du2~{F{ zx^l6eX!NyAWgb%_RQ7-DU<)g9u=trCvhYx4CjLSz@low{_mS-Yjgo%K!2ItUsXzUb z%4s5VpSn-MO99@lfAKZtaV`=_8*oPEx_kH}E?TJ10 zFZ+kY!Lh>bV+AYS&4wIEIj4ER%y(*RTdiDY$a)M2vpY@W;ZK-)^evUAi}~@ zxFVT4>&w;o{ipDxagb?w)A6ndtAVZZr%3Lr7Uakza_&%3PPQG7%lT~Y8SeT@i;qzK z$y2^z!v%bC9>oW@+tloT{d_Bs=FU$P384kPlrJ&Y64P6I18OpEke;=d03ElO z%aL@%Tu0ib8oT8yArSwq)5YV8yM)X+Lc`Fxv1cZrwL@}#71M+5Ux4WjlA$x@Trm7A zPmAn-)pMiIXP?`Iw7%of_V|;+hr?uv7M_R3FXJFtH!hzOic~L3r2uUK79zEM%zcvX9c5wF5K&${A!e%*&7i!3(469=}bnV zha!_cG?4HnNs*nSB8iuu>aGH5F+6$AzqkM>DRR$1y$6Y&Odfq4^%)&{FawTm;UZC@ zbvIWIdkwRW-y3`Ng)vhtN5PD%6v(iEKog_2BSlh%0b5duQf;4bzKAk4yjorRK;ckS zM?P&Gp3wt^fewixxz1e(Nz(q+Y8i?1yYXY4$AN`q{2dX|2ZmgDLj-jQBaV=MxTqgcX~ zrArI#cl6lqF~?x=*B6Avh_u-rpdIV@9JdGp1#M-b|E?|8%1SXo^pr8_D6Ba2F2r6& zM&_dpDFTquytph8Y3<6@m?jxj|u)lHi4 zP7FE-?Mglk9_nrY<{7hDu}5;KRB}i{v5V~fpf5hxyE<2cdOt6{#dALmyXB^s<*jT4 z0xH86ChZ^%NTW!ZnW7=`_7hDws+LI#gg_r`-|#iTEe<~ggm;d*#!5UBs5y{#ob?AU zs@O#fKN8+d@jQ*{yg35<47Ck0{$L*K5Y8XZLAIk5g%Qq?xh?I-NK*)dt-qtm@tIr8 zv`}n}t?lr3kU|kp$H;;En~{v`T&@>lf^$WHbkz*5hKxPrh={WJSFy_@Ic zb+xltvgK@fd_llr1;IaZoAGsAw$-F(1A+@{Y|*Xm!4fTV_zHxN`CP z<@sL#Pgf8`fum*232VLBdOQ9&MW41=V=gu$Cg8c#PztHnymlu+x-4w#a6!Jyi^_Y@is_B zg^XZsRJ~ttc-!K-(AoWlL>N{8+ETRL-`<_4XR!?368-Wp*d2BKQahkzP|4!B;8P5D z>*2^P;GO(q2u8nq0sGve3s$mQ0ecqS2nh6Wt5AMQMhQ4`=D>Q{#hY?z*E>V1u79u} zdMDP9?zgNN()k$wzhruN1R1r@!J3RtRB`7g{FQVUbcv?2aYiHodZZl0=1~`%MF#{f znyTFT5yaZGX)06h&50PF??Uelmsah$`H{=o(K;*3LZ^ z9&?Wr148hZGUX2Z-Fsh?I+0Ua##W8_T{>Ilc?Zp_Vs^W@Hcwl3fKgo6+%Tqy=2?#! zdp7%{53Qc6rNv2~%*xZ`33i?DoNU(A$WX+>#`cb%u{qd(7_=Wf?EDB+@$n14uC3(Q zkQiQwtb#V>r4zQ|sZm7DQ?vv8TVBHY&=tm5MTA&bXiCeX{Z!C<0 zNZNZs_wlhqPN+_x%A!T=5wj{&U-|85Si64#<#86^xwubz%1G_H5NFq~Fl`c@J59!j z#Ru-RFm1v3n|u1z+(rm!jJ?Ep3pcnbf155K^h5a`l1SUTwy>Vkc{E0$S91n&L~oy@T|S2(z}ge>cC4`;+Q zc87AQsjln%crguLPADz;(PzO|1BEE*VOY5zo4ImYG}GiqO72nfm+V^Z-b2_$0ENx49(ZntY_nWu zUQW>0X4kBL1W7h*7>Q_rMq}WxNNZ+G5h84V)-L-sPlj257Ap5#MaJKj<1ox< zj5vwS@y3|3o6rF>W|^-Gz>{Mrty$FsE}62FuqV93^W z!gpRdtJ~o?f7Q0>M7Oh>0oRgt9+$|q`$o}x4p!TNt2s^ce6Z`{5Y*!6XSEBXWS=n; zDdGRg+X29W34uabyH!Mkv-K9h^qQl5FJL-SdoBRtOLEwgkqM4`&+BoWmnvD|`O;#{w05_(k)2Md^8x$OX-<3Sy^lMrT7*`hGn?LJH@5mH~nR>39WeGJ( zqo|uVJ$hZ1j*Y?>XwOrrtS+qMCwZ=9s4XJQi3Sk!iCS=>Nd4I8G)z#mJXo>6CX3cbD zXC#Lzp7SpLYbxJr%c~?ma^9JBH~YI(cW6x*$YUwTmjxtn+8|H~iu=_D#dS|%XSTv9 zzU3(13{EULxLM=7h@1~&>-w=)R332>E|Pu;Rq77lksGC@>Hh+Ogrbg2O{4^BWrNJj ztIJT{*80vsQ&h+3Kh=Mzh&iWq@R;S?l@nLzKYPd8W@)GauYgT*Q7tdHxjHL)d4kR^ zqF+OBrJFuWj1rM`)MMV^c@y^clPbxlqtnwP`Y4Ua&=69uCVpxRk4%C92^5g5@Z1z> z=*wv2Q6214;8;|cSlP|Ew(!i+?uGC|<0ou&I))yJ{;k@UHKbE>Ap&Lm=!=P#0g-)a zYUMDQG)kH~nF&?`7@0Tea#<=FT6_Q1yu{JtagN6_rP2fkAypEOj+cs}Njx1g!Es+uj$hqI6$OWE=pLqQ-$iM9Er*aW zyHNiDX3N|w1=ST{k9(Ejj@kjQi;ab%mwTKw9i`8$Nlz+jAJ|LfM@EDP`43ftigF^> zrw`=~e*t073Fze2L5a}#bix*CCD_POTj!(KD+txzTL{}PV(tz*F;T;g`Ao!R#R=Zu zgTHA&+#UQWCo%KmKo|ZWF2Kl9`hgPLNsC?#B)ii*4m}f#-vW9qqyLmW%P5axFBgc4 zQPpl_aw;-I(V8G)i-jb1t3D?nDZrXp;$iLUw4W5L*s=Srr=|8yfUFd?oXl-6==1n3 z&#~Li_*0G@IJRg}Q{bEOlC!Sp8H1>EvM={+8?;7(QPAd3zsn!6Nv`80mmXu+=iQp} z!Wpf`P)LFn`v+?t0BT5!=${oWR3F2~fzk-%zy0j`yQ0h0}$$~YjTRClM>#q^!fH&C1F$PQw9 z`7)g)Jt#UCqZ2K162+l3Hu>;-GemaTz(*CEa>l!ZAq!6SI^EE~saMfI)tafw-$srO zO=UDdx-bOWM``WUQ}rpJ$iog)3s_LquwEC_|2QT$xA19Dnt<;?2_FeKaHzht^}DX8 z-?CuqI8s~rJKYTsGK^SLm`mgjqrewz3(rul=s}q&(YWfL$HH{mI-gM*Ln@0BTDqdJuerk=t_SJ=V9pY z$*$FAv)t0A#$Din;w{Gj^=a9YNZCREdpH=EfoM; zTE}3nP6>z%h2`}c`9AdQ{|hY%()M$kyFS2f{ne9RLQt6ZQO7xBl9YRnQBqXS8{%!6 zvfWzf>H%#n!}Vfo99knWy8*m(I3WGA=%m}@g09&}Dq)I7xq+^a0EV0b54YRhbv;>T zqSRKGP}Tg72YfQLe9=D`4j<%4aJi0@(-(;K4Rx}j7sVVfxzBjwH$RUof=3|y-;S%- zXu5_h+E=~Q;m_57DSdvYBR-yR%lTENYHgNn8UFxhl@}_FEld2y8>Cb)%`EOMs3nG1 zgBZXBM}eG^&UnJU)0DbNwM=ck!9_~{0GDj081i0QyJ+WeAPnai_sYjynS8U_V6@zr z$5BgO`Qnl}Lx7aV2IqXE2V(&Aesm9-Wev+@q8BEHCkTlqb@M8xV|x*533(nN41|Yb6i=wX$z3kXN?K@r1 zOkv!5;ZLLWEN@KlWd8u=eTA&j%hgklld$Nf(o*T#D@`p(sQzCADcm=2ywmEu=TODz>Lio9=bxIt@^rM*-Hkuz z91}@JxkM75RDNOgSj)*Yaf8aE4Ps;P*$LbXbQ1 zP$`sPpHx^6hoR9L*ki`y^JmocR`L$eXzOR<6Ye#{$gpRx6IKEbkRO)Ci%d}@cSBE*9gOje49?( z067lHU7nda#u8CV@6R3o0AFJ!rYW_!U#jgr*fWFDV8Kg$Yk zj<8&vsA#9Ef@(TwrfcPy!~z;R;0&DOE2ivd{(<`>%*VPi$e8E7#|>*+lH=$A>-JH7 zS0+!(821{|Mm)A@^fYv}_xP-JCTJ~}m|05sLo#4v9A6pBhB(`do|~1%fpa3BzS~at zgm$?IyUFLl)a01gFQ2EWN{vS5sYFy>*{;Cd_l z0${4O$y{P^xJd1lkAKwX0!SXio}T4Ps64-iamf|psoi~eMK}~aON;(~rT)|~l;)Ay zCo9ajv~)qr0mLEM7E%FXLY5^8ImzgZMDlQ=x!8#(q!fskn^VL+RhUuUnWelWc4a?97QAr^d;)`)b3|IjeNDf8{ zCjAm>H!~X!}Y0H0e z$K`VkPft-*XpTRcIq9&*G{z%%cH%JZGrSjLo<`6+F1q4NNXv$MwBV1i?78Ns()VZA zOC>dZeDF+5owcnl2ORDvl5#rxuIES8#}u4rD)Owst;FW7D>l|z*sCd8)=%ck6Bs2p z!Q5I8`l>Bg1hg8G*?epak}QUiEPJAn@txjT=eu6)j<`{LJ+se6a;msY%C`BCwmsDk zIe$6GZ~FSHO>1YUx>2@DDw>F@A|QrF;6wWMcL0x3f_f^U=;`&erTKr_{fRyKFk4)i zy%RGi^`*{9o+E~;7@EM=ox9798|{JX*oD!ET*}X;?@znH$?7fwHqInuNcmH@z)Cx1(plF;)*J$W@wS^jQKLThiM}? z!Ri6e+bgxWHSBX90c(K-9^pZwv)idEjZ82#ug>Ae4Lsu?Y?1j|@amLe)cJl2oFt^2 zT@=mm&qjxbI+C^E9O2g)i2nf0vNq>OW4=ul!XVEkV-vCrjtz6cX&A}I14er^l6$VO zU64RH_w-z&QrAZ|rMS;6GvN+?JfQI42w-E5PbVd${(Y628E%_%mkagsxc++))VVmX zBX+s&GtUr&)lSyMZ+oLImE1C_V>Vl!?Q?Q{g6O&io~j!o9b6<5I5n;F7+C$SZU*3a z&UbQoBMJ|PL;nD9iig1)BzxS`ARjxxar(Z*k@#9|>1(x8$tRE4jtnX*awiB_;68E6 zMAU$C9uz+?j_8c8J<8-u9Gl^qhfFvqysvc0k=~^wz22o87!sKQKhzVfq$jJ@f0-ce zQHqI2^%YJ`870|_urx>fWiB`!80Fben{k<~#xjHMGP$_gvCBuETs9(cZ@5EgRR*G5 z{Y5R6Q#bmOeKLL+5Xu=#z(9bzRsuUBqF7)!uMfQtd5)SZd&0zIJ{fRD7Hgz*6o0ZPDa1n$s=0xSe! z3@1Pc5SR^ej3akcK@Jx_eNtzks1 z!H%-gcWj=leRReQW8oc