1. 程式人生 > >前端筆記之React(八)上傳&圖片裁切

前端筆記之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