七牛在Meteor中的上传实例与FileReader的使用

七牛在Meteor中的上传实例与FileReader的使用

引言

自己在项目中使用qiniu的总结.
可以在这里下载Meteor-qiniu-demo

安装

qiniu通过 npm 以 node 模块化的方式安装:

meteor npm install qiniu

还需要这个包将异步转为同步:

meteor add meteorhacks:async

初始化
在自己的秘钥中找到 AccessKey / SecretKey.

找到文件

使用<input type="file"> 就可以创建一个上传uploader,然后要在meteor中找到file对象.

一个FileList对象通常来自于一个HTML input元素的files属性,你可以通过这个对象访问到用户所选择的文件.该类型的对象还有可能来自用户的拖放操作,查看 DataTransfer 对象了解详情.

<input id="fileItem" type="file"> // multiple 属性提供多文件上传
var file = document.getElementById('fileItem').files[0];

参考这里FileList

使用FileReader

在Meteor中,我们需要在client中获取 Dom中的files对象,所以就需要用到FileReader API去方便我们获取files.

FileReader
HTML5 终于为我们提供了一种通过 File API 规范与本地文件交互的标准方式.
可使用 File API 在向服务器发送图片的过程中创建图片的缩略图预览,或者允许应用程序在用户离线时保存文件引用。

然后通过fileReader将其读取到内存中.

FileReader 接口可用于通过熟悉的 JavaScript 事件处理来异步读取文件。因此,可以监控读取进度、找出错误并确定加载何时完成。这些 APIXMLHttpRequest 的事件模型有很多相似之处。

meteor中,client/main.js:

Template.upload.events({
  'change #uploader'(event, instance) {
    let node = document.getElementById('uploader').files[0];

    // 实例化 FileReader 对象,以便将其内容读取到内存中
    var reader = new FileReader();
    reader.readAsDataURL(node);
    reader.onload = function(event) {
      let dataUrl = event.target.result;
      
      console.log(typeof dataUrl);
      // DDP传的数据必须是EJSON-able的,无法传二进制,在server端使用qiniu上传时得转回二进制,而这里的dataURL是base64的
      Meteor.call('qiniu-upload', dataUrl, function(err, res) {
        if (!err) {
          alert('图片上传成功');
        }else {
          alert('图片上传失败,请重试');
        }
      });
    }
  },
});

使用qiniu NodeSDK

将秘钥中找到 AccessKey / SecretKey的配置在qiniu.conf
这里利用Async异步转同步,关于meteorhacks:async可以在这里获取更多信息.atmosphere-meteorhacks:async

server/qiniu-upload.js:

import qiniu from 'qiniu';
import {Meteor} from 'meteor/meteor';
import {check} from 'meteor/check';

const ak = 'AccessKey';
const sk = 'SecretKey';
const bucket = 'bucket';


Meteor.methods({
  'qiniu-upload'(dataUrl) {
    // 新版本的meteor要求method和publication函数都要check参数
    check(dataUrl, String);
    
    qiniu.conf.ACCESS_KEY = ak;
    qiniu.conf.SECRET_KEY = sk;

    // 华北地区的空间需要使用以下域名
    qiniu.conf.UP_HOST = 'http://up-z1.qiniu.com';

    // 由于Meteor.methods 不是异步函数,但是qiniu是异步,所以最好将两者变成一致,我这里利用Async变成同步
    let wrappedQiniuIo = Async.wrap(qiniu.io, ['put']);

    let putPolicy = new qiniu.rs.PutPolicy(bucket);
    let token = putPolicy.token();
    let extra = new qiniu.io.PutExtra();

    // qiniu上传图片需要图片的二进制数据
    let buffer = new Buffer(dataUrl.replace(/^data:image\/\w+;base64,/, ''), 'base64');

    let ret = wrappedQiniuIo.put(token, '', buffer, extra);
    console.log(ret.key);
  }
})

处理二进制

runoob.com http://www.runoob.com/nodejs/nodejs-buffer.html Buffer(缓冲区)
Buffer库为Node.js带来了一种存储原始数据的方法,可以让Nodejs处理二进制数据,
每当需要在Nodejs中处理I/O操作中移动的数据时,就有可能使用Buffer库。
原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

其他Node.js缓冲模块Buffer文章实例

qiniu上传也可以指定key去自定义上传到七牛后保存的文件名.参考 qiniu-NodeSDK

备注

通过 File API 使用 JavaScript 读取文件
Meteor-DDP 翻译文章
DDP 翻译文章

为什么要在method中将异步转同步?

在meteor中,客户端代码是异步,而服务端的method是同步函数,而qiniu sdk提供的则是异步函数,如果一个异步函数在同步函数中,同步函数没有等异步函数执行完就执行结束,那么有可能造成一些不好的影响.

在这个例子中,如果我要在server/qiniu-upload.js中的'qiniu-upload'(dataUrl) {...}ret.key直接插入到数据库中,那就有可能没有等ret返回,insert就结束了.

有关JS中异步与同步,在这篇文章中有详细讲解 JavaScript:彻底理解同步、异步和事件循环(Event Loop)

Show Comments