一、上傳
formidable天生可以處理上傳的文件,非常簡單就能持久上傳的文件。
今天主要講解的是,前后端的配合套路。
上傳分為同步、異步。同步公司使用非常多,異步我們也會講解。
1.1 先看一下antd中的Form和代碼校驗
我們看antd中的Form組件,https://ant.design/components/form-cn/
所謂的裝飾器指的是將一個組件作為一個函數的參數。
MyForm是一個標准的組件,要用Form.create()()裝飾一下。
const WrappedRegistrationForm = Form.create()(MyForm);
裝飾器,就是函數后面有兩個圈比如,getFieldDecorator()()。第一個圈里寫如何裝飾,第二個圈里寫被裝飾的組件。
慢慢去分析人家的API,慢慢看,慢慢琢磨。
1.2 上傳前端套路和formidable的實現
<input type="file"/>
我們現在要制作提交,前端界面必須滿足幾點:
1)必須要用form標簽嵌套,這是一個表單,不是一個ajax程序;
2)表單的method屬性必須是POST
3)必須有enttype,值是“multipart/form-data”,什么意思?下面有圖片講解
4) file控件必須有name屬性
5)必須有提交按鈕
<form action="/uploadAvatar" method="POST" enctype ="multipart/form-data"> <input type="file" name="avatar" /> <input type="submit" name="上傳"/> </form>
后端實現上傳非常簡單(當然工作實現上傳肯定不是你的事情):
//上傳圖片的路由 app.post("/uploadAvatar" , function(req,res){ var form = new formidable.IncomingForm(); //定義上傳的文件夾名字 form.uploadDir = path.resolve(__dirname , "./www/uploads"); //保留文件拓展名 form.keepExtensions = true; form.parse(req , function(err , fileds , files){ res.send("成功"); }); });
文件名會被隨機改名。
如何得到上傳之后的隨機名字?
form.parse(req , function(err , fileds , files){ console.log(files.avatar.path); res.send("成功"); });
提煉真正的文件名,而不包括完整路徑:
var base = path.parse(files.avatar.path).base;
1.3 抑制頁面跳轉
當點擊submit按鈕的時候,瀏覽器的默認邏輯就是跳轉到action的頁面。
除非你用異步。但是,異步Ajax瀏覽器很少支持,IE10開始支持。
<form action="/uploadAvatar" method="POST" enctype="multipart/form-data"> <input type="file" name="avatar" /> <input type="submit" name="上傳"/> </form>
在同步的時候,我們就要使用奇淫技巧。
使用iframe,框架,內嵌小電視。
<iframe src="http://www.baidu.com" frameborder="1"></iframe>
在小電視中的所有跳轉,都是關閉到小電視中的。
我們的表單就可以被內簽到小電視中,此時即使submit發生了跳轉,也是在小電視中的跳轉。
直接使用靜態頁面,在www里面創建pages文件夾:
一切都將發生在小電視中,頁面跳轉被抑制了。
1.4 上傳按鈕
並且我們希望用戶選擇完圖片之后,就能立即上傳,而不用多一次提交的點擊。
用模擬事件就行了
所有的內嵌頁面,將不能天生擁有外部的window的作用域。
alert($)
alert(window.parent.$)
jQuery有問題,只能得到引包的window域中的dom。
模擬事件:
<script> $("#pencile").click(function(){ $("#file_btn").trigger("click"); }); //一旦file被改變,就上傳 $("#file_btn").bind("change" , function(){ $("form")[0].submit(); }); </script>
1.5 回調函數
上傳成功之后調用父window中的js。
form.parse(req , function(err , fileds , files){ var base = path.parse(files.avatar.path).base; res.send("<script>window.parent.success_upload_avatar(" + base + ");</script >") });
二、圖片的裁切
2.1 gm的安裝
我們要安裝gm這個東西,它是C++寫出來的一個軟件,不是npm包。
http://www.graphicsmagick.org/
GraphicsMagick is the swiss army knife of image processing.
小而功能多
下載:ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/windows/
按自己的位數進行安裝:
將安裝路徑,上圖所示的這個文件夾的路徑,添加到系統的環境變量中
為了檢查環境變量是否設置正確,要輸入
gm -version
2.2 讓gm為nodejs工作
https://www.npmjs.com/package/gm
此時要安裝gm這個npm包
npm install --save gm
nodejs就能夠剪裁圖片、編輯圖片、美化圖片等等操作。
nodejs程序
var gm = require("gm"); //上傳圖片的路由 app.post("/uploadAvatar" , function(req,res){ var form = new formidable.IncomingForm(); //定義上傳的文件夾名字 form.uploadDir = path.resolve(__dirname , "./www/uploads"); //保留文件拓展名 form.keepExtensions = true; form.parse(req , function(err , fileds , files){ //圖片的物理路徑,指的是c:\node_study…… var pathwuli = files.avatar.path; //圖片的文件名 var base = path.parse(pathwuli).base; //查看上傳的文件的寬度、高度 gm(pathwuli).size(function(err, size){ console.log(size.width , size.height); res.send("<script>window.parent.success_upload_avatar('" + base + "');</script >") }); }); });
formidable是用來上傳圖片的
gm是用來處理圖片。這里使用了語法
gm().size(function(err,size){ })
可以得到圖片的尺寸。
后台要給你圖片尺寸,因為你不能耍無賴:讓圖片撐出盒子,又讓圖片被盒子約束。
上傳圖片,已經是先上傳圖片,然后在頁面上顯示具有URL服務器地址的圖片。
然后我們要有一個思維,就是用px來精確控制彈出層的樣子,這樣好看。
2.3 圖片的裁切邏輯
新瓶裝舊酒,jQuery的DOM邏輯還是非常豐富,React在這里只是一個殼子。

componentDidMount(){ var self = this; //四個信號量 var cutX = 0 , cutY = 0, cutW = 100 , cutH = 100; //cut里面的貓膩圖片,為了讓cut亮 var $cut_img = $(this.refs.cut_img); $(this.refs.cut).draggable({ containment : "parent", drag : function(event , ui){ cutY = ui.position.top; cutX = ui.position.left; $cut_img.css({ "left": -cutX, "top": -cutY }); //調用設置預覽圖的函數 setPreviews(); } }); //改變尺寸 $(this.refs.cut).resizable({ aspectRatio : 1 , containment : "parent", resize : function(evnet , ui){ cutW = ui.size.width; cutH = ui.size.height; //調用設置預覽圖的函數 setPreviews(); } }); //設置預覽路 function setPreviews(){ var bigimgW = $(self.refs.bigimg).width(); var bigimgH = $(self.refs.bigimg).height(); //批量設置 $(self.refs.priewBox).find(".pb").each(function(){ var w = $(this).data("w"); $(this).find("img").css({ "width": w * bigimgW / cutW, "left": -cutX / cutW * w, "top": -cutY / cutH * w }) }); } //裁切按鈕的事件監聽 self.cutBtnHandler = function(){ //圖片原寬和當前寬度的比 var rate = self.props.realw / $(self.refs.bigimg).width(); $.post("/docut" , { x: cutX * rate, y: cutY * rate, w: cutW * rate, h: cutH * rate, picurl: self.props.tanchucengPicUrl }); } }
后端接口

app.post("/docut" , function(req,res){ var form = new formidable.IncomingForm(); form.parse(req, function (err, fileds, files) { //得到前端發來的各種數據 const {x, y,w,h,picurl} = fileds; //完整路徑 var fullurl = path.resolve(__dirname , "./www/" + picurl); //裁切圖片 gm(fullurl) .crop(w,h,x,y) //注意這里的坑:參數的順序是寬、高、x、y。 .resize(180,180) .write(fullurl , function(err){ //覆蓋原圖即可 console.log(err) console.log("done"); }) }); })