前端使用 form 上传文件,要求限定上传的格式为图片。 改进前使用 accept:来限制文件类型,但是发觉这种限制比较弱,用户选择其他格式文件后,仍然可以提交非图片格式的文件到后端。
<%= form.file_field :tm_image, multiple:false,class:"form-control ",style:"width:300px;", accept:'image/png,image/gif,image/jpeg,image/jpg,image/tiff,image/bmp,image/svg' %>
解决方案分两个小点,一个是读取用户选取的文件的文件格式并判断是否图片格式,另一个是,在非图片文件格式时,提示用户。
<script type="text/javascript">
function checkImage(img) {
const realFileElement = document.querySelector("#realFileType"); //这里的realFileElement元素,对应为上述form中的input框下面为提示建立的div
const file = img.files[0] ///读取用户选取的文件
var filePath = img.value; ///读取用户选取的文件的路径
var fileExt = filePath.substring(filePath.lastIndexOf("."))
.toLowerCase(); ///截取用户选取的文件的后缀名
if (!checkFileExt(fileExt)) { ///如果文件后缀名检测不是图片
realFileElement.innerText = `所选“${file.name}”文件不是图片,请选择图片!`; ///innerText 方法,在提示div区域写入提示内容
img.value = ""; ///同时将input框的value设置为空,因为文件后缀名不是图片
return;
}
}
function checkFileExt(ext) { ///检测后缀名是否为图片格式的函数
if (ext.match(/.jpg|.gif|.png|.bmp|.jpeg|.tiff|.svg/i)) {
return true;
}
return false;
}
</script>
<%= form.file_field :tm_image, multiple:false,class:"form-control ",style:"width:300px;", accept:'image/png,image/gif,image/jpeg,image/jpg,image/tiff,image/bmp,image/svg',:onchange => "checkImage(this)" %>
///通过其他方式提示也可以,譬如 alert,但 alert 提示个人感觉,用户处理时需要移动眼球和鼠标,不如这种提示方法感知度好。
<div class="text-red" id="realFileType"></div> ///这里的 id="realFileType",对应的是上述JavaScript函数中的 const realFileElement = document.querySelector("#realFileType");
上述 JavaScript 函数通过读取用户选取的文件的后缀名来判断文件类型,存在一定的不足,譬如可以通过修改虚假的文件名后缀来骗过上述函数,用户可以手动修改文件后缀名以绕过格式限制,从而上传成功。上述方案的优点是,代码简单,不需要了解太高深的文件类型二进制数据判断知识,JavaScript 小白也能读懂,一般场景下对文件类型校验没有太高要求的,应用也基本足够。 更完善的解决方法是,读取文件二进制数据中的文件签名来精准判断文件类型,详见https://github.com/jealyn/real-file-type
上述方法,稍作改动也可以用于判断 docx、pdf 等格式,也能增加文件大小的判断,需要增加代码也不多。