[教程]如何使用APIG触发器上传文件,并通过函数工作流转储到OBS

举报
云端小王子 发表于 2018/08/28 19:29:34 2018/08/28
【摘要】 背景: 在一些应用场景中,希望通过函数和APIG构建一个无服务器的API,这个API接收用户上传的文件,然后对文件进行分析(比如对图片进行分类等),最后将文件转储到OBS。本文以Node.js函数为例,介绍两种方案供大家讨论。方法一:将文件的base64编码作为请求body体的参数传递。一般来说,现在绝大多数的API都会以Restful的形式提供,请求的body为json格式,且为了扩展性,...

背景: 在一些应用场景中希望通过函数和APIG构建一个无服务器的API这个API接收用户上传的文件然后对文件进行分析比如对图片进行分类等最后将文件转储到OBS。


本文以Node.js函数为例介绍两种方案供大家讨论。


方法一: 将文件的base64编码作为请求body体的参数传递。

一般来说现在绝大多数的API都会以Restful的形式提供请求的body为json格式且为了扩展性我们可以将文件内容base64编码作为json的某个字段传输。在前端文件的base64编码也非常方便比如在浏览器中可以用FileReader对象读取文件的base64编码内容。

详细代码如下以浏览器上传图片为例

/* 函数工作流-函数代码(Node.js) */

const https = require('https');
const fs = require('fs');
const ObsClient = require('esdk-obs-nodejs');    // 引用OBS SDK

let CONTEXT = null;
let IMAGE_BASE64 = '';
const PATH = '/tmp/image.png';


exports.handler = function (event, context, callback) {
    CONTEXT = context;
    
    // 跨域验证
    if (event.httpMethod === "OPTIONS") {
        console.log('OPTIONS request, pass.');
        const result = {
            body: '',
            headers: {
                "Content-Type":"application/json",
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Headers": "Content-Type,Accept",
                "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE"
            },
            statusCode: 200,
            isBase64Encoded: false
        };
        callback(null, result);
        return;
	}

    // 从请求体中获取文件内容
    IMAGE_BASE64 = getImageBase64(event);
    const token = context.getToken();
    /**
     * 处理上传的文件
     * ......
     */
    
    // 上传文件到OBS
    const bucket = context.getUserData('BUCKET') || '';
    saveImage(IMAGE_BASE64, bucket).then((err) => {
        // return apig result
        if (err) {
            const result = {
                body: 'success',
                headers: {
                    "Content-Type":"application/json",
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Headers": "Content-Type,Accept",
                    "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE"
                },
                statusCode: 200,
                isBase64Encoded: false
            };
            callback(null, result);
        } else {
            const result = {
                body: err,
                headers: {
                    "Content-Type":"application/json",
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Headers": "Content-Type,Accept",
                    "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE"
                },
                statusCode: 500,
                isBase64Encoded: false
            };
            callback(null, result);
        }
    });
}

function getImageBase64(event) {
    // 因为apig封装的body是base64编码过后的所以需要先解码获得json的结构体
    const bodyStr = new Buffer(event.body, 'base64').toString('ascii');
    try {
        const body = JSON.parse(bodyStr);
        return body.image || '';
    } catch (ex) {
        console.log('Invalid parameter');
        return '';
    }
}

function saveImage(imageBase64, bucket) {
    return new Promise((resolve, reject) => {
        writeImage(imageBase64).then((err) => {
            if (!err) {
                uploadImage(PATH, bucket).then((err) => {
                    resolve(err);
                });
            } else {
                resolve(err)
            }
        });
    });
}

function writeImage(image) {
    return new Promise((resolve, reject) => {
        const imageBuffer = new Buffer(image, 'base64');
        fs.writeFile(PATH, imageBuffer, function(err) {
            console.log('write file:', err);
            resolve(err);
        }); 
    });
}

function uploadImage(path, bucket) {
    return new Promise((resolve, reject) => {
        // upload file to bucket;
        const client = buildOBSClient(CONTEXT);
        client.putObject({
            Bucket: bucket,
            Key: `image-${(new Date()).valueOf()}.png`,
            SourceFile: path
        }, (err, result) => {
            if (err) {
                console.error('Upload Error-->' + err);
            } else {
                console.log('Upload Status-->' + result.CommonMsg.Status + '. uploading file OK!');
            }
            resolve(err);
        });
    });
}

function buildOBSClient(context) {
    // Obtains a temporary AK and SK to access OBS. An agency is required to access IAM.
    const my_access_key = context.getAccessKey();
    const my_secret_access_key = context.getSecretKey();
    console.log('begin create obs client!');
    return new ObsClient({
        access_key_id: my_access_key,
        secret_access_key: my_secret_access_key,
        server: 'obs.cn-north-1.myhwclouds.com'
    });
}


前端相关代码如下

<!-- html -->

<input
    type="file"
    accept="image/*"
    multiple={false}
    onChange={this.handleChange}
/>
/* js */

function handleChange(e) {
    const files = e.target.files;
    if (files.length === 0) {
        return;
    }
    const file = files[0];
    uploadImage(file);
}


function uploadImage(file) {
    const reader = new FileReader();
    // encode the file to base64
    reader.readAsDataURL(file);
    reader.onload = () => {
        // strip the header, keep pure base64 data
        let content = reader.result;
        
        const cpIndex = content.search(',');
        content = content.substr(cpIndex + 1);
        const body = {
            image: content;
        }
        
        // post(body);    此处发送请求的方法很多如通过ajax、fetch...就不细写
    };
}


方法二: 通过表单上传文件

当然也可以直接使用表单的方式上传文件它的本质还是一个http请求只不过body有自己规定的结构

表åæ交çæ°æ®图片转自链接

详情可参考W3 和 《RFC 1867 -Form-based File Upload in HTML


APIG只做请求的转发请求最原始的数据会作为函数的event参数的字段传入函数所以处理方式和方法一无太大差别。那么函数的event.body对象即是上图结构数据的base64编码在函数中我们解析一下body数据即可。详细如下,以浏览器上传一个文件为例:


/* 函数工作流-函数  相关解析代码(Node.js) */

const bundary = event.headers['Content-Type'].split('boundary=')[1];    // 边界分割符
console.log('bundary is', bundary);

const bodyStr = new Buffer(event.body, 'base64').toString('ascii');
const fileInfo = bodyStr.split(`--${bundary}\r\n`)[1].split(`\r\n--${bundary}--`)[0].split('\r\n\r\n');
// fileInfo[0] :  文件信息
// fileInfo[1] :  文件内容
<!-- html -->

提交表单
<form action="###" enctype="multipart/form-data;charset=utf-8" method="post" id="MyForm" accept-charset="UTF-8">
	文件:<input type="file" name="file"/><br/>
	<input type="button" onclick="clickMe();" value="提交"/>
</form>

<script type="text/javascript" src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
	function clickMe(){
		var form=document.getElementById("MyForm"),
		formData=new FormData(form);
		$.ajax({
			url:'APIG 触发器 URL',
			type:'POST',
			data:formData,
			processData:false,
			contentType:false,
			success:function(data){
				alert(data);
			}
		})
	}
</script>


总结

APIG支持各种方式上传文件因为它只做转发最终保持客户端的HTTP请求信息触发函数执行函数会拿到最基本的网络传输数据由开发者根据需要自己处理。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。