|
| 1 | +# js-web-uploader |
| 2 | +基于html5的文件上传工具 |
| 3 | + |
| 4 | + |
| 5 | + |
| 6 | +## 安装 |
| 7 | + |
| 8 | +```sh |
| 9 | +npm install js-web-uploader --save |
| 10 | +``` |
| 11 | + |
| 12 | +## 注意 |
| 13 | + |
| 14 | + * 该上传工具仅包含JavaScript部分 |
| 15 | + * 支持文件分块上传、断点续传,秒传 |
| 16 | + * 内部http请求使用XHR(XMLHttpRequest)对象,文件分块基于Blob.prototype.slice |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | +## 使用说明 |
| 21 | +使用时需创建一个Uploader实例,假设实例为uploader: |
| 22 | +* 批量上传:uploader.upload() |
| 23 | +* 单个文件上传 uploader.queue[0].upload() |
| 24 | +* 批量暂停:uploader.abort() |
| 25 | +* 单个文件上传 uploader.queue[0].abort() |
| 26 | +* 文件状态 uploader.queue[0].status (详见附录2) |
| 27 | + |
| 28 | +### 普通上传 |
| 29 | + |
| 30 | +```js |
| 31 | +import { Uploader } from 'js-web-uploader'; |
| 32 | +const uploader = new Uploader({ |
| 33 | + inputElem: 'uploadInput', // 选择文件的元素 |
| 34 | + uploadUrl: '',// 文件上传路径 |
| 35 | +}) |
| 36 | +``` |
| 37 | + |
| 38 | +### 分块上传 |
| 39 | +##### 分块上传需计算文件hash值,并将其作为文件的唯一标识,可参考以下思路: |
| 40 | + * 客户端上传文件分块,同时携带所属文件hash值、文件分块数量、分块索引(第几个分块) |
| 41 | + * 服务器接收文件分块并保存分块信息,文件以hash值作为唯一标识 |
| 42 | + * 服务器在每个分块上传后检测是否该文件所有分块已经上传,若已上传所有分块,则将分块合并成文件保存,并将获取文件的url返回给客户端 |
| 43 | + |
| 44 | +```js |
| 45 | +import { Uploader } from 'js-web-uploader'; |
| 46 | +const uploader = new Uploader({ |
| 47 | + inputElem: 'uploadInput', // 选择文件的元素 |
| 48 | + uploadUrl: '',// 文件上传路径 |
| 49 | + // 填写chunkSize参数触发分块上传 |
| 50 | + chunkSize: 1 * 1024 * 1024, // 分块大小,单位byte,可以为数值或function,function参数qf为队列文件(详见附录1) |
| 51 | + // chunkSize: qf=>1 * 1024 * 1024, |
| 52 | + uploadType: 0, // (分块)上传方式 0-并行上传 1-串行上传 默认值:0 |
| 53 | + data:{}, // 上传时携带的数据,值可以是对象或funtion(详见附录1) |
| 54 | + }) |
| 55 | +``` |
| 56 | + |
| 57 | +### 断点续传 |
| 58 | + |
| 59 | +断点续传基于分块上传,即服务器保存文件已上传分块信息,在上传前获取文件上传状态,将未上传的文件分块继续上传 |
| 60 | +(继续上传时调用upload方法) |
| 61 | + |
| 62 | +```js |
| 63 | +import { Uploader } from 'js-web-uploader'; |
| 64 | +const uploader = new Uploader({ |
| 65 | + // ... 省略其他参数 |
| 66 | + /** 检测文件上传状态方法 */ |
| 67 | + checkMethod: (qf, resolve) => { |
| 68 | + // 请求文件上传状态 |
| 69 | + // 调用 resolve 方法告知已上传分块,传参为数组 例:resolve([0,1,2]) |
| 70 | + } |
| 71 | + }) |
| 72 | + |
| 73 | +``` |
| 74 | + |
| 75 | +### 秒传 |
| 76 | +在checkMethod里向服务器获取文件上传状态 |
| 77 | +* 若文件已完成上传,则服务器直接返回文件访问url,客户端在checkMethod里resolve所有分块 |
| 78 | +* 若未完成上传,则在checkMethod里resolve已上传分块 |
| 79 | + |
| 80 | +```js |
| 81 | +import { Uploader } from 'js-web-uploader'; |
| 82 | +const uploader = new Uploader({ |
| 83 | + // ... 省略其他参数 |
| 84 | + secondUpload:true, |
| 85 | + checkMethod: (qf, resolve) => { |
| 86 | + // ... 请求文件上传状态 |
| 87 | + // 已完成上传 |
| 88 | + // 调用 resolve 方法告知已上传分块,若分块数量为3,则:resolve([0,1,2]) |
| 89 | + resolve([0,1,2]) |
| 90 | + } |
| 91 | + }) |
| 92 | + |
| 93 | +``` |
| 94 | +### API |
| 95 | +* upload() 上传文件 |
| 96 | +* setOptions(options) 设置参数 |
| 97 | +* remove(index) 删除指定下标的文件(queue中的qf) |
| 98 | +* on(event,callback) 注册监听事件,详见附录 |
| 99 | +* trigger(event,...args) 触发指定事件,arg为传入的参数 |
| 100 | + |
| 101 | +```js |
| 102 | + // 文件选择时 |
| 103 | + uploader.on('select', files => { |
| 104 | + // files-选择的文件列表 |
| 105 | + }) |
| 106 | + // 移除文件前 |
| 107 | + uploader.on('beforeRemove', qfs => { |
| 108 | + // qfs- qf列表 |
| 109 | + // 若返回值为 false, 0, '', null中的任一值,则将不执行移除操作 |
| 110 | + // 若返回值为Promise实例且resolve false, 0, '', null中的任一值,则将不执行移除操作 |
| 111 | + }) |
| 112 | + // 移除文件后 |
| 113 | + uploader.on('remove', qfs => { |
| 114 | + // qfs- qf列表 |
| 115 | + }) |
| 116 | + // 超出数量时 |
| 117 | + uploader.on('countExceed', (totalCount, maxCount) => { |
| 118 | + // totalCount 总数量 |
| 119 | + // maxCount 最大数量 |
| 120 | + }) |
| 121 | + // 单个文件超出大小限制时 |
| 122 | + uploader.on('sizeExceed', (eFiles, maxSize) => { |
| 123 | + // eFiles 超出大小的文件 |
| 124 | + // maxSize 文件大小最大值限制 |
| 125 | + }) |
| 126 | + // 生成分片前 |
| 127 | + uploader.on('beforeChunk', qf => { |
| 128 | + }) |
| 129 | + // 生成分片后 |
| 130 | + uploader.on('beforeChunk', qf => { |
| 131 | + }) |
| 132 | + // 生成文件hash值前 |
| 133 | + uploader.on('beforeHash', qf => { |
| 134 | + }) |
| 135 | + // 生成文件hash值后 |
| 136 | + uploader.on('beforeHash', qf => { |
| 137 | + }) |
| 138 | + // 进度事件 |
| 139 | + uploader.on('progress', (ev, percent) => { |
| 140 | + // ev事件对象 |
| 141 | + // percent 单个文件上传百分比 |
| 142 | + }) |
| 143 | + // 上传成功 |
| 144 | + uploader.on('success', (qf,res,chunk) => { |
| 145 | + // qf 详见附录1 |
| 146 | + // res 服务器返回结果 |
| 147 | + // chunk分块上传时的分块 |
| 148 | + }) |
| 149 | + // 上传失败 |
| 150 | + uploader.on('error', res => { |
| 151 | + // res 发生错误时的结果 |
| 152 | + }) |
| 153 | +``` |
| 154 | + |
| 155 | +## 附录1 options |
| 156 | + |
| 157 | +options |
| 158 | +```js |
| 159 | + // 示例 |
| 160 | + const uploader = new Uploader({ |
| 161 | + inputElem: 'uploadInput', // 选择文件的元素 |
| 162 | + triggerEvent: 'chuange', // 触发事件 默认change |
| 163 | + uploadUrl: '', // 文件上传路径 |
| 164 | + name: 'file', // 上传的文件字段名 |
| 165 | + hashFile: true, // 是否计算文件hash值 (默认md5) |
| 166 | + hashMethod: (file) => { |
| 167 | + // 可选计算文件hash值的方法,file为文件对象 |
| 168 | + // 需返回一个Promise实例,并resolve计算后的hash值 |
| 169 | + }, |
| 170 | + uploadType: this.uploadType, // 分块上传时上传方式 0-并行上传 1-串行上传 |
| 171 | + chunkSize: (qf) => 1 * 1024 * 1024, // 分块大小 单位byte 数值或function qf为队列文件 |
| 172 | + headers: (info) => { |
| 173 | + // 请求头 键值对象或返回键值对象的function,info为分块信息 |
| 174 | + return { |
| 175 | + // ... |
| 176 | + } |
| 177 | + }, |
| 178 | + data: (info) => { |
| 179 | + // 上传时携带的数据,需返回一个对象 |
| 180 | + return { |
| 181 | + // ... |
| 182 | + } |
| 183 | + }, |
| 184 | + /** 检测文件上传状态方法 */ |
| 185 | + checkMethod: (qf, resolve) => { |
| 186 | + // 请求文件上传状态 |
| 187 | + // 调用 resolve 方法告知已上传分块,传参为数组 例:resolve([1,2,3]) |
| 188 | + } |
| 189 | + }) |
| 190 | +``` |
| 191 | +## 附录2 文件状态 |
| 192 | +即uploader.queue[0].status |
| 193 | +```js |
| 194 | +/** 队列文件状态 */ |
| 195 | +export const QF_STATUS = { |
| 196 | + pending: 'pending', // 等待上传 |
| 197 | + ready: 'ready', // 准备就绪 |
| 198 | + hashing: 'hashing', // 正在计算hash值 |
| 199 | + hashed: 'hashed', // 计算hash值完成 |
| 200 | + chunking: 'chunking', // 正在进行文件分块 |
| 201 | + chunked: 'chunked', // 文件分块完成 |
| 202 | + checking: 'checking', // 检测文件状态 |
| 203 | + checked: 'checked', // 检测文件状态完成 |
| 204 | + uploading: 'uploading', // 正在上传 |
| 205 | + abort: 'abort', // 中断 |
| 206 | + success: 'success', // 上传成功 |
| 207 | + error: 'error' // 上传失败 |
| 208 | +} |
| 209 | +``` |
| 210 | + |
| 211 | +## 附录3 qf |
| 212 | +qf为保存在Uploader实例的queue属性(数组)中的对象,qf是QueueFile的实例 |
| 213 | +```js |
| 214 | + qf = { |
| 215 | + file: null, // 原始文件对象 |
| 216 | + fileHash: '', // 文件哈希值 |
| 217 | + chunkSize: 0, // 分块大小 |
| 218 | + chunks: [ |
| 219 | + // 文件分块信息 |
| 220 | + // { |
| 221 | + // index: 0, // 第几个分块 |
| 222 | + // blob: null, // 分块bob数据 |
| 223 | + // percent: 0, // 上传进度 |
| 224 | + // uploaded: false // 是否已上传 |
| 225 | + // } |
| 226 | + ], |
| 227 | + percent: 0, // 上传百分比 |
| 228 | + status: 'pending', // 状态 见附录2 |
| 229 | + response: '' // 服务器返回 |
| 230 | + // ... |
| 231 | + } |
| 232 | + |
| 233 | +``` |
| 234 | + |
| 235 | +## 附录4 chunk |
| 236 | +文件分块信息,chunk是Chunk的实例 |
| 237 | +```js |
| 238 | +class Chunk { |
| 239 | + queueFile // qf |
| 240 | + index = 0 // 分块索引 |
| 241 | + blob // 分块blob数据 |
| 242 | + percent = 0 // 上传百分比 |
| 243 | + uploaded = false // 是否已经上传 |
| 244 | + response = null // 服务器的返回值 |
| 245 | + // ... |
| 246 | +} |
| 247 | + |
| 248 | +``` |
| 249 | + |
| 250 | +## 附录5 info |
| 251 | +```js |
| 252 | + info = { |
| 253 | + file: null, // 原始文件对象 |
| 254 | + fileHash: '', // 文件哈希值 |
| 255 | + chunkSize: 0, // 分块大小 |
| 256 | + chunks: [ |
| 257 | + // 文件分块信息 |
| 258 | + // { |
| 259 | + // index: 0, // 第几个分块 |
| 260 | + // blob: null, // 分块bob数据 |
| 261 | + // percent: 0, // 上传进度 |
| 262 | + // uploaded: false // 是否已上传 |
| 263 | + // } |
| 264 | + ], |
| 265 | + percent: 0, // 上传百分比 |
| 266 | + status: 'pending', // 状态 见附录2 |
| 267 | + response: '', // 服务器返回 |
| 268 | + chunk: null, // 分块的blob数据 |
| 269 | + chunkIndex: 0 // 分块索引 |
| 270 | + |
| 271 | + // ... |
| 272 | + } |
| 273 | + |
| 274 | +``` |
| 275 | + |
0 commit comments