上个月写过一篇 WordPress 通过 Rest API 自定义附件上传接口,那篇文章主要介绍了如何打通前后端,利用 Rest API 保存“抓取”已知 URL 的文件。不过在更多的情况下,我们还是需要通过自己的表单上传文件。正好这个月有个项目,有个表单需要拖放文件到指定区域里,提交表单的时候上传文件。如果我用 Gravity Forms 给的文件上传域,它只有原生的点击选取本地的文件组件,并没有拖放选取文件的组件,所以这里就需要造一个轮子了。先说结果,最后实现出来是这样子的:
文件拖到区域上方,区域会变色;拖入后区域内显示文件名,判断文件格式和尺寸是否合规;点击清除,会重置区域。
这里面#pro_document 这个就是真实的用来上传的输入项,把它隐藏,通过 JS 使.js-drag 整个区域可以接受拖放文件进来
<div class="dragarea js-drag">
<p>DRAG A FILE HERE,<br/>OR</p>
<span class="js-drag-fileinfo"></span>
<a href="###" class="button">Choose a File</a>
<a href="###" class="js-drag-clear">x Clear</a>
<input type="file" value="" name="pro_document" id="pro_document" placeholder="" class="js-drag-file" />
</div>
$(function(){
//允许的格式
var fileTypeArray = ['JPG','PNG','CAD','RAR','ZIP'];
function FileListItems (files) {
var b = new ClipboardEvent("").clipboardData || new DataTransfer()
for (var i = 0, len = files.length; i<len; i++) b.items.add(files[i])
return b.files
}
function FileVerify(file){
if(file.size > 2*1024*1024){
alert("文件不能大于2M");
return false;
}else if(file.size <= 0){
alert("文件大小不能小于0");
return false;
}else{
var arr = file.name.split('.');
var suffix = arr[arr.length-1].toUpperCase();
if(fileTypeArray.includes(suffix)){
return true;
}else {
alert("暂时不支持" + suffix + "格式文件");
return false;
}
}
}
var dragElement = $('.js-drag');
//拖入结束
$('.js-drag').on("drop", function(event){
event.preventDefault();
event.stopPropagation();
var fileFieldDOM = $(this).find('.js-drag-file')[0];
var fileFieldDisplay = $(this).find('.js-drag-fileinfo');
console.log(event);
event.dataTransfer = event.originalEvent.dataTransfer;
var firstFile = event.dataTransfer.files[0];
var firstFileArr = [ firstFile ];
var newfileList = new FileListItems(firstFileArr);
if( FileVerify(firstFile)){
console.log("file verified: "+ firstFile.name);
console.log(firstFile);
//.filled样式追加后,区域变成已添加文件的状态
$(this).addClass("filled")
fileFieldDisplay.html("selected file:</br>" + firstFile.name);
fileFieldDOM.files= newfileList;
}
$(this).removeClass("dragenter");
});
//点击开启选择文件窗口
$('.js-drag').on("click", function(event){
var fileFieldDOM = $(this).find('.js-drag-file')[0];
var fileFieldDisplay = $(this).find('.js-drag-fileinfo');
fileFieldDOM.click();
});
//拖入
$('.js-drag').on("dragenter", function(event){
event.preventDefault();
event.stopPropagation();
//console.log('dragenter');
//增加样式.dragenter,其实就是一个背景色,以提示用户有文件正在拖进来
$(this).addClass("dragenter");
});
//文件正在区域上方
$('.js-drag').on("dragover", function(event){
event.preventDefault();
event.stopPropagation();
});
//拖离
$('.js-drag').on("dragleave", function(event){
//console.log('dragleave');
$(this).removeClass("dragenter");
});
//所选的文件改变
$('.js-drag-file').on("change",function(event){
var firstFile = $(this)[0].files[0];
if( FileVerify(firstFile)){
console.log("file verified: "+ firstFile.name);
console.log(firstFile);
$(this).parent().addClass("filled");
$(this).siblings('.js-drag-fileinfo').html("selected file:</br>" + firstFile.name);
}else{
$(this)[0].files=new FileListItems("");
}
});
//清空选定的文件
$('.js-drag-clear').on("click",function(event){
event.preventDefault();
event.stopPropagation();
$(this).siblings('.js-drag-file')[0].files=new FileListItems("");
$(this).siblings('.js-drag-fileinfo').html("");
$(this).parent().removeClass("filled");
});
});
.form-regular .dragarea{
box-sizing: border-box;
width: 100%;
height: 150px;
background:#f7f7f7;
border-radius: 6px;
border:2px dashed #d3d3d3;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content:center;
font-size: 13px;
text-align: center;
}
.form-regular .dragarea.dragenter{
background:#dedede;
}
.form-regular .dragarea p{
font-size: 13px;
margin:0 auto 6px auto;
line-height: 1.3;
}
.form-regular .dragarea .button{
margin:0 auto;
}
.js-drag-file{
overflow: hidden;
width: 1px;
height: 1px;
opacity: 0;
}
.js-drag .js-drag-fileinfo,
.js-drag .js-drag-clear{
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;
}
.js-drag .js-drag-fileinfo:first-line{
color: #b2b2b2;
}
.js-drag.filled .js-drag-fileinfo,
.js-drag.filled .js-drag-clear{
display: block;
}
.js-drag.filled p,
.js-drag.filled .button{
display: none;
}
还是通过 Rest API 保存文件,保存后获取到文件地址以做后续其他工作。注意要上传文件就必须要身份验证,身份验证的接口上一篇文章也说了,先安装Basic-Auth插件。
//通过Rest API保存文件
function brain_wp_api_upload( $filedata ){
$file_url = $filedata['tmp_name'];
$file_content=file_get_contents($file_url);
$url_api = site_url("/wp-json/wp/v2/media/");
$file_name = $filedata['name'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url_api );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_POST, 1 );
curl_setopt($ch, CURLOPT_POSTFIELDS, $file_content);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'content-disposition: attachment; filename="'.$file_name.'"',
'authorization: Basic ' . base64_encode('username:password')
//'authorization: ' . $_SERVER['HTTP_AUTHORIZATION']
)
);
$result=curl_exec($ch);
if(json_decode($result)->id){
return json_decode($result)->guid->rendered ;
}else{
return json_decode($result)->data->status . " " .json_decode($result)->message;
}
}
//获取提交的内容,并调用brain_wp_api_upload这个函数返回的文件URL
if(!empty($_FILES['pro_document']['tmp_name'])){
$pro_document = brain_wp_api_upload( $_FILES['pro_document'] );
echo "pro_document: ".$pro_document. "<br/>";
}else{
$pro_document = "";
}
...
//后续的一系列处理
最后,因为是对外使用的上传功能,还需要单独开设个子目录上传文件单独保存,以免和文章等其他附件混淆在一起。用 upload_dir 这个钩子,通过识别上传的用户 id 临时修改目录:
add_filter( 'upload_dir', 'brain_change_upload_dir_ct' );
function brain_change_upload_dir_ct( $upload ) {
if( get_current_user_id()== 2 ){//basic认证的用户id
$upload['subdir'] = '/subfolder'; //子目录名
$upload['path'] = $upload['basedir'] . $upload['subdir'];
$upload['url'] = $upload['baseurl'] . $upload['subdir'];
}
return $upload;
}
其实后来找了下 Gravity Forms 也有实现类似功能的外挂,比如 这个,前端是原生的也有,比如 这个。可惜这些都不完全符合我的应用场景。在提交表单之前,我只想让用户先选定本地文件,而并不是立即上传,我表单里还有很多其他选项以及用了验证码系统,在表单提交后通过验证,再上传文件,这样可以避免网站被滥传一些无用的文件甚至是木马。
专业提供WordPress主题安装、深度汉化、加速优化等各类网站建设服务,详询在线客服!