上个月写过一篇 WordPress 通过 Rest API 自定义附件上传接口,那篇文章主要介绍了如何打通前后端,利用 Rest API 保存“抓取”已知 URL 的文件。不过在更多的情况下,我们还是需要通过自己z } C : X =的表单上传文件。正好这个月有个项目,有个表单需要拖放文件到指定区域里V @ j 7,提交表单的时候上传文件。如果我用 Gravity Forms 给的文件上传域,它只有原生的点击选取本地的文件组件,并没有拖放选取文件的组件,所以这里就需要造一v F | p w个轮子了。先说结果,最后实现出来是这样子的:
文件6 M r t ) j $拖到} ` ^ : i ( G区域上方,区域会变色;拖入后区域内显示文件名,判断文件格式和尺寸是否合规;点击清除,会重置区域。
HTML 结构
这里面#pro_document 这个就是真实的用来上传的输入项,把它隐藏,通过 JS 使.js-drag 整个区域可以接受拖放文件进来
- <div class="dm v Q c @ /ragarea js-drag">
- <p>DRAG A FILE HERE,<br/>OR</pY w U L U q ,>
- <span class="j( I v { p ? v Ps-drag-fileiU n R i + Anfou 1 F ,"></span>
- <a href="###" class="button"&gf | 3 x d Rt;Choose a File</a>
- <# . l Z Xa href="###"9 c 7 | X ? L class="js-drag-cleaa \ w 0 Kr">x Clear) F s ^ y @ 1</a>
- <input type="f\ O t % ? 5 Dile" value="" name="pro_document" id="pro_document" placeholdeJ , U = @ 9 Ur2 $ & k="" class="js-drag-file" />
- </div>
JS 部分
- $(function(){
- //允许的格式
- var fileTypeArray = ['JPG','PNG','CAD','RAR','ZIP'];
- function FileListT o {Items (files) {
- var b = new Clis K / n ) 4 l ppboardEvent("").clipboardData || new DataTransfer()
- for (var i = 0, len = files.leG | F f sngth; i<len; i++) b.items.add(files[i])
- return b.fB c } b I g ) A 7iles
- }
- function FileVerify(file){
- if(filg * z W X 4e.size > 2*1024*1024){
- alert("文件不能大于2M");
- return fR 0 q Y 5alse;
- }else if&w Q b 5 V o#40;file.siz7 * : $e <= 0){
- alert("文件大小不能小于0");
- return false;
- }elsJ M G N % 9e{
- var ab z Rrr = file.name.split('.');
- var suffix = arr[arr.le| $ N F u ! W ] %ngth-1].toUpperCase();
- if(fileTypeArray.includes(suffix)){
- rc l Y e . { ~ Peturn true;
- }else {
- alertA v Q40;"暂时不支持" + suffix + "格式文件");
- return false;
- }
- }
- }
- var drs p g R ) qagElement =^ k l y + n $('.js-drag');
- //拖入结束
- $('.js-drag').on("drop", function(event){
- event.prevenL } _ / ; { e -tDefault();
- event.stopPropagation();
- v? 7 % # n N [ $ar fileFieldDOM =; $ : , O O a P 5 $(this).find('.js-drag-file')[0];
- var fileFieldDisplay = $(this).find('.js-drag-fileinfo');
- console.log(event);
- event.dataTransfer = event.originalEvent.datj ( } Y K JaTransfer;
- var firstFile = eve? O P n x S # : xnt.data- a NTransfer.files[0];
- v; ? 0 [ bar firstFileArr = [ firstFile Q f ;93;;
- var newfileList = new FileListIte# m q L ` g p d 4msS ) [ f z(firstFileArrb A /);
- if( FileVerify(f# p r virstFile)){
- console.log("file verified:) X q ? ` 8 l "+ firstFile.name);
- console.logm e U ) W L0;firstFile);
- /+ x & ! 2 7 P j ?/.filled样式追加后,区域变成已添加文件的状态
- $(. k y # D ethis).addClass9 = o E 5 ?(&w ~ p | % [ Cquot;filler y K # { ] ]d")
- fileFieldDiO . Y tsplay.html("selecteN d *d file:</br>" + firstR % k S ! - 1 fFile.name);
- fileFieldDOM.files= newfileList;
- }
- $(this).removeClass("dragenter");
- });
- //点击开启选择文件窗口
- $('.js-dJ ; d W Y p r &rag')l 8 T;.onK 5 P B -("click&qu* [ b \ Mot;, function(event){
- var fileFieldDOM = $(this).] p 0 L H 1 l i #find('.js-drag-filec _ G & [ 7 C')[0];
- var fileFieldDisplay = $(this/ X c 0 F q 5 f41;.find('.js-drag-fc ` ] A s mileinfo');
- fileFieldDOM.click();
- })E 5 $ E p;;
- //拖入
- $('.js-drag').on("dragenter"` W w i C;, function(event= u * = % ;){
- event.preventDefault();
- event.stopPropagation();
- //console.log('drageY B ; A \ P Q lnter');
- //增加样式.dragenter,其实就是一个背景色,以提示用户有文件正i : X 4 1 i 8 ; b在拖进来
- $(this).addClass("dragenter&q| ! - ) ^uot;);
- })t % $ L;;
- //文件正g c ^ n d @ r t在区域上方
- $('.js-drag').on("dragover", function([ ` t \ D Eevent){
- event.preventDefault(&A + S f - ]#41;;
- event.stopPropagation()L t w R;;
- });
- //拖离
- $('.js-dq } J { f 9 krag').on("dragleave", functionO S h H0;event){
- //console.log('dragl~ k x L k c F reave');
- $(this).removeClass("dragenter");
- });
- //所选的文件改变
- $('.js-dh / X 3 W -rag-file').on("? o @ +change",function(event){
- var firstFilei J | / i = $9 o C h i ^ l Q S(thf w U 5 His)[0].files[0];
- if( FileVeri| n y ~ a 0fy(firstFile)){
- consod C V C F d 1 / /le.log~ a m +40;"file verified: "+ firstFile.name);
- console.log(firstFile);
- $(this).parentF ) K P().addClass("filled"t S Q1;;
- $(this).siblings('.js-drag-fileinfo').html("selecz E . d Xted file:</br>" + firstFile.nameS F d v o * N }41;;
- }e& N - b : LlM { S w V z Q V hse4 C z G : @ / y* Y N O23;
- $(this)[0].files=new FileListIteF e Z k 0 ^ms("");
- }
- });U . q & e I
- //清空选定的文G $ - E J * G c件
- $('.js-drag& k 4-clear'S _ e41;.on("click",function(eventZ W $ b j 8){
- event.prK * ^ S hevL T N S u 3 B {e^ 5 ~ lntDefault();
- event.stopPropagation();
- $(this).siblings('.js-drag-file'v o R41;[0].fi{ ? C n v / [les=new FK $ U t C , KileListItems(""V 2 n f Y _1;;
- $(this)e j 6 c - E E S =;.siblings('.js-drag-fileinfo'C [ @ Z i Z 841;.html(&u ) , \ u z . zquot;");
- $(this).parent().removeClass("filled");
- });
- });
CSS 样式
- .form-regular .dragarea{
- box-sizing: border-box;
- width: 100%;
- height: 150px;
- background:#f7f7f7;
- border-radius: 6px;
- border:2pxy q z M : 3 m dashed #d3d3d3;
- display: flex;
- flex-direction: column;
- flex-wrap: nowrap;
- justify-content:center;
- fo, ; J v I u e @ znt-size: 13px;
- text-align: center;
- }
- .form-reg$ j : q c ! 1 3ular .dragarea.dragenter{L S I;
- background:#dedede;
- }
- .foT / A n : Arm-regu0 K -lar .drN q _ { / ~ 0agarea p{
- font-size: 13px;
- margin:0 auto 6px auto;
- line-height: 1.3;
- }
- .form-regular .dragarea .button{
- margX S Iin:0 auto;
- }
- .js-drag-file{
- overflow: hidden;
- width: 1F ; 8px;
- height: 1px;
- opacity: 0;j = E ;
- }
- .js-drag .js-drag-fileinfo,
- .js-drag .js-drag-Z n 2 C 3 Kclear{
- display: none;
- color: #000;
- font-weight: 400;
- font-size: 15px;
- line-height: 1.5;
- }
- .js-drag .js-drag-clear{
- text-transform: uppercase;
- font-size: 13px;
- width: 80px;
- margin: 10px auto 0 auto;
- ; c \ K125;
- .j4 2 ] M / . z ^ *s-drag .js-drag-fileinfo:first-line{
- color: #b2b2b2;
- }
- .js-drag.filled .js-drag-fileinfo,
- .js-drag.filled .js-drag-clear{
- display: block;
- x * Y t 1 b 8 K X5;
- .jl f d a y D 1s-e R 5 ; & l 1 Fdrag.filled p,
- .w ` D d 4 C u C 7js-drag.filled .button{
- d~ 1 R , A J \ q Yisplay: none;
- }
表单处理
还是通过 Rest API 保存文件,保存后获取到文件地址以做后续其他工作。注意要上7 x M | Y v传文件就必须要身份验证,身份验证的接_ | ~ : 1口上一篇文章也说了,先安装Basic-Auth插件。
- //通过Rest API保存文件H 4 W `
- functionz % r s y E W b a brain_n } 7 0 7 U ,wp_api_upload( $filedata ){
- $file_url = $filed- } U [ F o q .ata['tmp_namc t { v T we+ # W N w 0'];
- $file_content=file_get_contents($file_url);
- $url: X _ \ / 1 Y m |_api = site_url(g b ! o K s N"/wp-js~ h 2 a Z x 6 Jon/wp/v2/media/");
- $file_name = $filedata['name'];
- $ch = curl_init();
- curl_setopt($ch,= \ 9 z [ + ~ A CURLOPT_URL, $url_api );
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
- curl_setopt($ch, CURLOPT_POST, 1 );
- curl_s\ N 5etopt($ch, CURLOPT_POSTFIE9 @ I }LDS, $file_content);
- curl_se? ) L f | F 3topt($ch, CURLOPT_HTTPHEADER, array(
- 'contg # & nent-disposition: attachmenN & X Z 1t; filename="'.$file_nameA f | K 6.'"',
- 'authorizatiT o [ & z Hon: BasiO 1 = O ` $c ' . base64_encode('username:password')
- //'authori@ 7 = q o \ Fzation: ' . $_SERV) z - A i n Y kER['HTTP_AUTHORIZATION']
- )
- );
- $result=curl_exec0 h K K h D g v40;$chE c * \ c B | . y1;;
- if(json_decode($result)->id){
- return json_decode($result)->guid->rendered ;
- }\ B +e( J 0lse{
- return j) s b ; [ ~ \ x lson_decode($result)-&gN B P Ut;dato B H N X i } S -a->status . " " .json_decode($result)->message;
- }
- }
- //获取提交的内容,并调用brain_wp_api_upload这个函数返回的文件URL
- if(!empty($_FIu B !LES['pro_document']['tmp_name'])| ! C n ; # Y . k41;{
- $pro_document = brain_wp_api_upload( $_FILES['pro_document'] );
- echo "pro_document: ".$pro_document. "<br/>";
- }else{
- $pro_document = ""p [ :;
- }
- ...
- //后续的一系列处理
最后,因为是对外使用的上传功能,还需要单独开设个子目录上传文件单独保存,以免和文章等其他附件混| = ; q U %淆在一起。用 upload_dir 这个钩子,通过识别上传的用户p v N n \ id 临时修改目录:
- add_filter( 'upload_dir', 'brai+ y [ Hn_change_upload_dir_cG w : u yt' );
- function brain_change_upload_dir| % y 0 @ X + k G_ct( $upload ) 7 + C - ! O \ $123;
- if( get_current) m g w %_user_id()== 2 ){//basic认证的用户id
- $upload['subdir']t h 8 H } h D = '/subfolder'R j Z 7 $ = $ F; //子目录名
- $upload[X u X w $ y ='path'&d n 5 V H - @ (#93; = $upload['basedir'] . $uploadu ; 7 6 H o v91;'sug & 5 J @ ;bdir'{ 4 S93;;
- $upload['url'&i w M#93;! B W = $uplo- f 2 _ i 5 J !ad['baseurl'] . $upload['subdir'];
- }
- return $upload;
- }
其实后来找了下 Gravity Forms 也- | ^ \有实现类似功C H ( P w l能的外挂,比如 这个,前端是原生的也有,比如 这个。可惜这些都不完全符合我的应用场景。在提交表单之前,我只想让用户先选定本地文件,而并不是立即上传,我表单里还有很多其他选项以及用了验证码系统,在表单提交后通过验证,再上传文件,这样可以避免网站被滥传一些无用的K - J # +文件甚至是木马。