前端筆記之React(八)上傳&圖片裁切
一、上傳
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"); }) }); })示例程式碼
&n