之前,就听过“跨域上传”图片的问题,只是疏于研究,也就一再搁置,直至今天再次遇见这个不能避免的“坑”,才不得不思考一下,怎么“跨域上传”图片或者文件?
问题来源:
何为“跨域”? ——就是给你一个接口,外面暴露的url(并非是自己项目中的url),然后你发post()请求,请求给你的接口,请求成功,接口就会返回给你想要的结果。
实际情况:
我们公司自己做的项目一般都是使用nodejs的thinkjs框架(ThinkJS 是一款使用 ES6/7 特性全新开发的 Node.js MVC 框架,使用 ES7 中 async/await
,或者 ES6 中的 */yield
特性彻底解决了 Node.js 中异步嵌套的问题。)之前我们的上传图片都是显示在自己项目本地,而这次的需求却加上了请求另一个人的接口地址,然后正常上传图片。
页面HTML主要代码:
...
<label style="fmargin-top: 10px;width: 100px" ></label>
<div id="addImg">
<span class="addImglist"></span>
<img id="addPic" style="float: left;margin-top: 20px;margin-left: 20px" width="150px" height="100" src="/static/admin/img/addimg.jpg" onclick="addImg()">
</div>
<input type="file" name="uploadFile" id="fileupload_input" style="display: none"/> <div class="temp" style="float: left;margin-top: 20px;margin-left: 20px">
<img src="" class="showImg" ondblclick="canceImg(this)" width="150px" height="100"/>
<input type="hidden" class="imgs" name="imgs"/>
</div>
...
<script>
function addImg() {
uploadimg('dynamic');
} function uploadimg(type) {//这里的图片上传分为两种形式:动态以及用户头像
var url='';
if(type=='dynamic'){
url="/tools/uploadutils/uploadtonet?type=dynamic&t=" + new Date().getTime();//文件上传地址 请求接口地址
}else{
url="/article/article/upload?type=portrait&t=" + new Date().getTime();//文件上传地址
}
jQuery('#fileupload_input').click().fileupload({
dataType: 'json',
url: url,
done: function (e, result) {
if (result.result.errno==0) {
var data=result.result.data;
if(type=='dynamic'){
jQuery(".temp:first").clone().appendTo('#addImg .addImglist');
if(jQuery(".temp").length>=10){
jQuery("#addPic").hide();
}
jQuery('#addImg .showImg:last').attr("src",data.path);
jQuery('#addImg .imgs:last').val(data.savePath);
}else{
jQuery('#portrait').attr("src",imgsite+"/static"+data.path);
jQuery('#img').val(data.savePath)
}
} else {
jQuery.messager.alert('提示', "上传失败");
}
}
});
} function canceImg(me) {
jQuery(me).parent().remove();
if(jQuery(".temp").length<10){ //只能上传九张图
jQuery("#addPic").show();
}
}
</script>
后台项目中的js代码:
uploadutils.js(文件路径:/tools/uploadutils/的uploadtonet方法):
/**
* Created by *** on 2016/11/10
*/
'use strict'; import Base from '../base.js';
import imgutil from '../../../common/util/imgutil';
import fs from 'fs';
import request from 'request'; export default class extends Base {
/**
* 上传图片给前台接口(c#程序)
* @returns {Promise|*|void|PreventPromise}
*/
async uploadtonetAction() {
let type = this.get("type");
if (!think.isEmpty(this.file('uploadFile'))) {
let savePath = "";//保存在数据库的路径
let file = think.extend({}, this.file('uploadFile'));
let fPath = file.path;
let suffix = fPath.substr(fPath.lastIndexOf(".") + 1);
if (suffix == "jpg" || suffix == "png" || suffix == "jpeg") {
let apiBaseUrl = this.config("apiUrl");
let reqUrl = apiBaseUrl + "/upload.ashx"; //c#接口请求地址
let fileObj = imgutil.getCSharpImageUrl(this.param("type"), suffix);
let path = fileObj.path + fileObj.fileName;
let dbUri = "/" + path; //数据库保存的路径
let req = think.promisify(request.post);
let options = {
url: reqUrl,
method: "post",
formData: {
file: fs.createReadStream(fPath),
path: path
}
};
let res = await req(options);
let result = JSON.parse(res.body);
let imgUrl = this.config("apiImgsite") + dbUri; //回显的路径
if (result.status == 1) {//上传成功
savePath = result.data.join(',');
return this.success({
path: imgUrl,
savePath: savePath
});
} else {
return this.fail();
}
} else {
return this.fail("上传图片格式有误,请重新上传!");
}
}
} //跨域请求的方法
call = async function (url, fPath, path) {
let req = think.promisify(request.post);
let reqObj = {
url: url,
method: "post",
formData: {
file: fs.createReadStream(fPath),
path: path
}
};
return req(reqObj);
};
}
主要的问题出在哪里呢???其实主要知识点就是在下:
这段代码是老大给的,为此还被骂了一顿(这段代码很难理解吗?其实也不然,有时候就是觉得自己的脑子在代码运行方面实在不怎么灵光!明明自己知道的东西,因为粗心或者不自信总是犯错,导致一些不可挽回的“形象破坏”):
XMLHttpRequest Level 2添加了一个新的接口FormData
.利用FormData对象
,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()
方法来异步的提交这个"表单".比起普通的ajax,使用FormData
的最大优点就是我们可以异步上传一个二进制文件。(https://developer.mozilla.org/zh-CN/docs/Web/API/FormData)
在网上找到了一个C#实现http协议GET、POST请求 ,觉得挺好的 http://blog.chinaunix.net/uid-7552018-id-173395.html
const options = {
method: 'POST',
uri: testData.url + `uploadprofilephoto`,
formData: {
image: fs.createReadStream('/home/rje/photo.jpg')
}
};
const json: IResponse<string> = await request(options);
uri:就是要请求的图片上传地址;
formData:模拟表单提交,接口需要两个参数,一个文件路径,一个文件名,以键值对的形式传给它,最终便会返回给你想要的东西了。
只是在此项目中,使用await request(option)得不到接口返回的结果,于是只能使用thinkjs自带的 think.promisify() —— think.promisify将一个异步函数自动改造,返回一个promise对象以供调用。
1.npm中request-promise模块(https://www.npmjs.com/package/request-promise),有具体的用法;
2.这次需求改动总结的小经验:
点击“添加图片”的时候,自动往后面添加一个相同的上传图片的点击框,即:
自己写的代码总是冗杂繁余,而其他人写的代码一看却是那么的简洁明了,不得不怀疑自己的能力。而自己想长期快乐的继续自己的这份工作时,就应该好好的沉淀自己,把自己培养成像同事一样的大神。