当前位置: 首页 > 编程日记 > 正文

上传图片,多图上传,预览功能,js原生无依赖

最近很好奇前端的文件上传功能,因为公司要求做一个支持图片预览的图片上传插件,所以自己搜了很多相关的插件,虽然功能很多,但有些地方不能根据公司的想法去修改,而且需要依赖jQuery或Bootstrap库,所以我就想学下图片上传的原理,试着做一个原生无依赖,而且足够灵活的图片上传插件。话不多说,开整。

1. 大体思路

1.1 首先我们需要考虑用户如何使用我们的插件。

用户引入插件代码后,只需要像下面这样,设置一些参数,然后执行一个方法就生成一个图片上传组件。

<div id="upload"></div> // 这是用来生成图片上传组件的div
<script>
// 设置参数 var options = { path: '/', // 上传图片时指定的地址路径,类似form变淡的action属性 onSuccess: function (res) { // 上传成功后执行的方法,res是接收的ajax响应内容 console.log(res); }, onFailure: function (res) { // 上传失败后执行的方法,res是接收的ajax响应内容 console.log(res); } } // 执行生成图片上传插件的方法, 第一个参数是上面提到的准备生成组件的div选择器,第二个参数是设置的组件信息,执行方法后返回一个函数指针,指向执行上传功能的函数,通过把执行上传功能的函数暴露出来,用户就可以自己控制何时上传图片了。 var upload = tinyImgUpload('#upload', options); </script>

1.2 代码设计

我们需要思考用户如何引入我们的插件代码。
插件代码应该分为两个文件,一个CSS文件(tinyImgUpload.css),用于定义组件的基本样式,此外用户可以根据自己的想法自己DIY样式,另一个是控制功能逻辑的js文件(tinyImgUpload.js)。用户引入这两个文件后,就可以实现图片上传组件了。

2. 具体实现

具体实现的时候,主要涉及到两个地方,一个是读取本地文件,实现图片上传前可以预览的功能,一个是图片上传功能。

2.1 读取本地文件实现预览

这里用到了html5的File API,使用这个API可以在客户端验证上传的文件类型,限制文件大小,当然,在这里我们主要用到FileReader接口来读取文件,Filereader.readAsDataURL()返回的事件对象的result属性就是将文件编码为base64的数据地址,类似下面这样的,把他赋值给src属性,图片就显示出来了。

具体代码如下,完整代码可以从这里下载(tinyImgUpload.js )

// 预览图片
//处理input选择的图片
function handleFileSelect(evt) { var files = evt.target.files; for(var i=0, f; f=files[i];i++){ // 过滤掉非图片类型文件 if(!f.type.match('image.*')){ continue; } // 过滤掉重复上传的图片 var tip = false; for(var j=0; j<(ele.files).length; j++){ if((ele.files)[j].name == f.name){ tip = true; break; } } if(!tip){ // 图片文件绑定到容器元素上 ele.files.push(f); var reader = new FileReader(); reader.onload = (function (theFile) { return function (e) { var oDiv = document.createElement('div'); oDiv.className = 'img-thumb img-item'; // 向图片容器里添加元素 oDiv.innerHTML = '<img class="thumb-icon" src="'+e.target.result+'" />'+ '<a href="javscript:;" class="img-remove">x</a>' ele.insertBefore(oDiv, addBtn); }; })(f); reader.readAsDataURL(f); } } } // input#img-file-input是一个隐藏的上传图片的input控件,当选择图片的时候会触发change事件 document.querySelector('#img-file-input').addEventListener('change', handleFileSelect, false);

2.2 上传图片

2.2.1 准备文件对象

上传文件之前,我们需要考虑如何保存用户已经选择的文件对象,由于用户可能多次选择,也可能在上传之前又删除了几个图片,所以需要有一个地方实时保存图片信息,并且要和预览的图片保持同步,预览显示有哪几张图片,这个地方就存储几张图片。我采用的方式是将文件信息组装成一个数组,然后绑定到组件元素(#img-container)的自定义属性上,上面代码中的“ele.files.push(f)”做的就是这件事。

2.2.2 文件对象我们准备好后,下一步就是上传了

ajax是不能直接上传文件对象的,我们可以通过FormData对象,FormData是XMLHttpRequest Level 2添加的一个新接口,使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest异步发送这个"表单"。具体代码如下。

// 上传图片
function uploadImg() { var xhr = new XMLHttpRequest(); var formData = new FormData(); for(var i=0, f; f=ele.files[i]; i++){ formData.append('files', f); } xhr.onreadystatechange = function (e) { if(xhr.readyState == 4){ if(xhr.status == 200){ options.onSuccess(xhr.responseText); }else { options.onFailure(xhr.responseText); } } } xhr.open('POST', options.path, true); xhr.send(formData); }

2.3 设置样式

我们写了一个基本的布局样式作为默认样式,用户可以根据自己的需求进行DIY。这里是完整的样式文件(tinyImgUpload.css )。
基本的效果图如下。

如果我们想触发上传图片,可以把tinyImgUpload('#upload', options)返回的upload方法绑定到一个按钮上,监听点击事件。

<button class="submit">submit</button> <script> document.getElementsByClassName('submit')[0].onclick = function (e) { upload(); } </script>

这样当我点击图片的时候,图片就会上传,交给服务器端处理了。

上传按钮

服务器接收的图片

为了测试图片上传好不好用,我自己搭建了一个图片接收的服务器,使用的是node.js,通过multer实现,如果大家感兴趣可以点击这里。

3 总结

图片上传的关键部分就是如何读取本地文件实现预览,以及通过FormData对象构造一个表单对象实现ajax异步上传文件。目前这个插件的功能还不够完善,我把它放到了Github上(https://github.com/gitwd/tinyImgUpload),后续会慢慢优化,欢迎大家提出宝贵意见。

相关文章:

springboot 简单自定义starter - beetl

使用idea新建springboot项目beetl-spring-boot-starter 首先添加pom依赖 packaging要设置为jar不能设置为pom<packaging>jar</packaging> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web&…

cmake生成so包并调用(C++project,build,cmake)

1. 目录结构 2 . downloads 2.1 build module process CMakeLists.txt > cmake_minimum_required(VERSION 3.5)if(CMAKE_COMPILER_IS_GNUCC)message("COMPILER IS GNUCC")ADD_DEFINITIONS ( -stdc11 ) endif(CMAKE_COMPILER_IS_GNUCC)SET(CMAKE_CXX_FLAGS_DEBU…

人眼模板匹配自动跟踪

void trackEye(cv::Mat& im, cv::Mat& tpl, cv::Rect& rect) {// 人眼位置cv::Size pSize(rect.width * 2, rect.height * 2);// 矩形区域cv::Rect tRect(rect pSize - cv::Point(pSize.width/2, pSize.height/2));tRect & cv::Rect(0, 0, im.cols, im.rows);…

前端碎碎念 之 nextTick, setTimeout 以及 setImmediate 三者的执行顺序

『前端碎碎念』系列会记录我平时看书或者看文章遇到的问题&#xff0c;一般都是比较基础但是容易遗忘的知识点&#xff0c;你也可能会在面试中碰到。 我会查阅一些资料并可能加上自己的理解&#xff0c;来记录这些问题。更多文章请前往我的个人博客这个问题是有关执行顺序和Eve…

bat 将war文件转换成ear文件

1、无需拷贝war文件&#xff0c;自动获取war set path%path%;D:\jdk\jdk1.6.0_31\bin;C:\Program Files\7-Zip del **0001-controller.war del **0001-controllerEAR.ear copy ..\target\**0001-controller-0.0.1-SNAPSHOT.war **0001-controller.war rem 7z d -tzip **0001-co…

cmake语法【一】

一、Cmake 简介 cmake 是一个跨平台、开源的构建系统。它是一个集软件构建、测试、打包于一身的软件。它使用与平台和编译器独立的配置文件来对软件编译过程进行控制。 二、常用命令 指定 cmake 的最小版本 cmake_minimum_required(VERSION 3.4.1)这行命令是可选的&#xff…

RHEL6.3安装vsftpd

1、下载vsftpd&#xff0c;可以从官网&#xff08;http://vsftpd.beasts.org&#xff09;下载&#xff0c;也可以百度搜索[rootlocalhost vsftpd]# wget http://down1.chinaunix.net/distfiles/vsftpd-3.0.2.tar.gz2、解压压缩包并进入解压出来的目录[rootlocalhost vsftpd]# t…

创建mysql数据库,在新数据库中创建表,再尝试删除表

创建之前&#xff0c;先登录数据库存 mysql -u 账号 -p密码 登录完成后&#xff0c;展示一下已存在的数据库 show databases; 创建数据库 create database test111; 然后展示一下数据库&#xff0c;如下 show databases; 使用数据库 use test; 在test数据库里面看一下已存在的…

cmake:在各级目录之间共享变量(cmake cache变量)

摘要: 本文记录一下 CMake 变量的定义、原理及其使用。CMake 变量包含 Normal Variables、Cache Variables。通过 set 指令可以设置两种不同的变量。也可以在 CMake 脚本中使用和设置环境变量。set(ENV{} …)&#xff0c;本文重点讲述 CMake 脚本语言特有的两种变量。 正文&am…

antd+dva笔记

参考 React中函数式声明组件Dva Ant Design 前后端分离之 React 应用实践ReactDvaJS 之 hook 路由权限控制dva 知识地图react-router Guides and API docs (v2, v3)react-sage redux-saga 是一个用于管理 Redux 应用异步操作,可以用来代替 redux-thunk 中间件。《Redux-saga 中…

求两个矩形重叠部分的面积

#include<stdio.h> #include<math.h>#define areaFile "area.txt" #define perportionFile "perportion.txt"#define min(a,b) ( ((a)>(b)) ? (b):(a) ) #define max(a,b) ( ((a)>(b)) ? (a):(b) )typedef struct xy { int x; int y; …

session,cookie,sessionStorage,localStorage的区别及应用场景

浏览器的缓存机制提供了可以将用户数据存储在客户端上的方式&#xff0c;可以利用cookie,session等跟服务端进行数据交互。 一、cookie和session cookie和session都是用来跟踪浏览器用户身份的会话方式。 区别&#xff1a; 1、保持状态&#xff1a;cookie保存在浏览器端&#x…

鱼眼镜头及其标定

1. 鱼眼镜头特性与镜头分类 普通镜头和针孔相机在数学模型上可以等价对待&#xff0c;都是射影变换&#xff08;Perspective transform&#xff09;&#xff1b; 鱼眼镜头受到水下斯涅耳窗口现象的启发&#xff0c;采用不同的投影方式&#xff0c;来得到极大的视场角&#xff…

django -- url 的 name 属性

在html的form中使用给url定义的name值&#xff0c;可以在修改url时不用在修改form的src。 urls.py from django.conf.urls import url from mytest import viewsurlpatterns [# url(r^admin/, admin.site.urls),url(r^index/, views.index, namemysite), views.Index.as_view(…

两个矩形重叠部分面积

#include<stdio.h> #include<math.h> #define min(a,b) ( ((a)>(b)) ? (b):(a) ) #define max(a,b) ( ((a)>(b)) ? (a):(b) )typedef struct xy { int x; int y; }xy;void main() {xy a[4];int s,chang,kuang;while (true){printf("Please input 4 x,…

前百度面试官整理的——Java后端面试题(一)

2019独角兽企业重金招聘Python工程师标准>>> List 和 Set 的区别 List , Set 都是继承自 Collection 接口 List 特点&#xff1a;元素有放入顺序&#xff0c;元素可重复 &#xff0c; Set 特点&#xff1a;元素无放入顺序&#xff0c;元素不可重复&#xff0c;重复元…

vibe前景提取算法示例代码

//ViBe.h#pragma once #include <iostream> #include "opencv2/opencv.hpp"using namespace cv; using namespace std;#define NUM_SAMPLES 20 //每个像素点的样本个数 #define MIN_MATCHES 2 //#min指数 #define RADIUS 20 //Sqthere半径 #define SUBSAMPL…

Linux系统程序运行时加载动态库路径顺序

程序运行时加载动态库路径顺序(Linux) 在linux系统中&#xff0c;如果程序需要加载动态库&#xff0c;它会按照一定的顺序&#xff08;优先级&#xff09;去查找: 链接时路径&#xff08;Link-time path&#xff09;和运行时路径&#xff08;Run-time path&#xff09;不是一回…

浮动元素会引起的问题和你的解决办法

问题&#xff1a; &#xff08;1&#xff09;父元素的高度无法被撑开&#xff0c;影响与父元素同级的元素&#xff08;2&#xff09;与浮动元素同级的非浮动元素会跟随其后&#xff08;3&#xff09;若非第一个元素浮动&#xff0c;则该元素之前的元素也需要浮动&#xff0c;否…

Visual Paradigm 教程[UML]:如何使用刻板印象和标记值?(下)

下载Visual Paradigm最新试用版 已加入在线订购&#xff0c;现在抢购立享特别优惠>> 将构造型应用于模型元素 接下来&#xff0c;我们将构造型应用于模型元素。右键单击Customer&#xff0c;然后从弹出菜单中选择Stereotypes> External User。 从图形上看&#xf…

基于opencv的简单视频处理类示例

#include "opencv2/opencv.hpp" using namespace std; using namespace cv; class VideoProcessor { private: VideoCapture caputure; //图像处理函数指针 void (*process)(Mat &,Mat &); bool callIt; string WindowNameInput; string WindowNa…

flex数据绑定

2019独角兽企业重金招聘Python工程师标准>>> 1 、方法绑定 [Bindable(event"myFlagChanged")] private function isEnabled():String { if (myFlag)return true; else return ‘false; } <mx:TextArea id"myTA" text"{isEnabled()}&…

【error】error: field * has incomplete type

在编译程序是出现了如题错误&#xff0c; 类或结构体有前向声明的用法&#xff0c;编译到这里时还没有发现定义&#xff0c;不知道该类或者结构的内部成员&#xff0c;没有办法具体的构造一个对象&#xff0c;所以会报错。 两种解决方法&#xff1a; 方法一&#xff1a;将类成员…

Web前端学习笔记:Vue生命周期理解

一、感谢原创博主 示例代码出处vue2.0 探索之路——生命周期和钩子函数的一些理解 官方文档 二、生命周期简单描述 总共分为8个阶段创建前/后&#xff0c;载入前/后&#xff0c;更新前/后&#xff0c;销毁前/后。 创建前/后 在beforeCreated阶段&#xff0c;vue实例的挂载元素…

获取结构体中变量的偏移量

C/C获取结构体中变量的偏移量 1.某些特殊需求下&#xff0c;我们需要知道某个变量在其结构体中的偏移位置。 通常的做法就是定义一个宏变量&#xff0c;如下&#xff1a; #define OFFSET(structure, member) ((int64_t)&((structure*)0)->member) // 64位系统 #defin…

VS2010 CUDA 5.5 VA_X Win7 64位配置

一.安装CUDA5.5以及配置VS助手 1、安装之前必须确认自己电脑的GPU支持CUDA。在设备管理器中找到显示适配器&#xff08;Display adapters)&#xff0c;找到自己电脑的显卡型号&#xff0c;如果包含在http://www.nvidia.com/object/cuda_gpus.html的列表中&#xff0c;说明支持…

SmartRoute之大规模消息转发集群实现

为什么80%的码农都做不了架构师&#xff1f;>>> 消息转发的应用场景在现实中的应用非常普遍&#xff0c;我们常用的IM工具也是其中之一&#xff1b;现有很多云平台也提供了这种基础服务&#xff0c;可以让APP更容易集成相关功能而不必投入相应的开发成本。对于实现…

unity项目警告之 LF CRLF问题

unity中创建的脚本&#xff0c;以LF结尾。 Visual studio中创建的脚本&#xff0c;以 CRLF结尾。 当我们创建一个unity脚本后&#xff0c;再用VS打开编辑保存后&#xff0c;这个文件既有LF结尾符&#xff0c;也有CRLF结尾符。 解决办法&#xff1a;更改unity的代码生成模板&…

Eigen向量化内存对齐/Eigen的SSE兼容,内存分配/EIGEN_MAKE_ALIGNED_OPERATOR_NEW

1.总结 对于基本数据类型和自定义类型&#xff0c;我们需要用预编译指令来保证栈内存的对齐&#xff0c;用重写operator new的方式保证堆内存对齐。对于嵌套的自定义类型&#xff0c;申请栈内存时会自动保证其内部数据类型的对齐&#xff0c;而申请堆内存时仍然需要重写operat…

c/c++文件遍历

//CBrowseDir.h#pragma once#include <stdlib.h> #include <direct.h> #include <string.h> #include <io.h> #include <stdio.h> #include <iostream> using namespace std; class CBrowseDir { protected: //存放初始目录的绝对…