標籤: 租車

  • 【K8s學習筆記】K8s是如何部署應用的?

    【K8s學習筆記】K8s是如何部署應用的?

    本文內容

    本文致力於介紹K8s一些基礎概念與串聯部署應用的主體流程,使用Minikube實操

    基礎架構概念回顧

    溫故而知新,上一節【K8S學習筆記】初識K8S 及架構組件 我們學習了K8s的發展歷史、基礎架構概念及用途,本節講的內容建立在其上,有必要把之前的架構小節提出來回顧下:

    K8s架構分為控制平台(位於的Master節點)與執行節點Node

    控制平台包含:

    • kube-apiserver(訪問入口,接收命令)

    • etcd(KV數據庫,保存集群狀態與數據)

    • kube-scheduler(監控節點狀態,調度容器部署)

    • kube-controller-manager(監控集群狀態,控制節點、副本、端點、賬戶與令牌)

    • cloud-controller-manager(控制與雲交互的節點、路由、服務、數據卷)

    執行節點包含:

    • kubelet(監控與實際操作容器)

    • kube-proxy(每個節點上運行的網絡代理,維護網絡轉發規則,實現了Service)

    • 容器運行時環境CRI(支持多種實現K8s CRI的容器技術)

    接下來需要引入 Pod 與 Service 的概念,這也是在上一篇文章中未給出的

    Pod、Service與Label概念

    Pod概念與結構

    Pod 是 K8s最重要的基本概念,官網給出概念:Pod是Kubernates可調度的最小的、可部署的單元。怎麼理解呢?最簡單的理解是,Pod是一組容器。

    再詳細些,Pod是一組容器組成的概念,這些容器都有共同的特點:

    • 都有一個特殊的被稱為“根容器”的Pause容器。Pause容器鏡像屬於K8s平台的一部分
    • 包含一個或多個緊密相關的用戶業務容器。假設個場景:業務容器需要獨立的redis提供服務,這裏就可以將它們兩個部署在同一Pod中。

    下邊是Pod的組成示意圖:

    為什麼Kubernetes會設計出一個全新的概念與Pod會有這樣特殊的結構呢?

    • 原因之一:K8s需要將一組容器視為一個單元處理。當其中某些容器死亡了,此時無法判定這組容器的狀態。而當成一個單元,只要其中有一個容器掛了,這個單元就判定掛了。
    • 原因之二:通過Pause共享容器IP,共享Pause掛接的Volume,簡化密切關聯的業務容器之間的通信問題和文件共享問題

    K8s為每個Pod都分配了唯一的IP地址,稱為Pod IP,一個Pod里的多個容器共享Pod IP地址。需要牢記的一點是:在 kubernetes 里,一個 Pod 里的容器與另外主機上的 Pod 容器能夠直接通信。

    Pod的創建流程

    當一個普通的Pod被創建,就會被放入etcd中存儲,隨後被 K8s Master節點調度到某個具體的Node上並進行綁定(Binding),隨後該Pod被對應的Node上的kubelet進程實例化成一組相關的Docker容器並啟動。

    當Pod中有容器停止時,K8s 會自動檢測到這個問題並重啟這個 Pod(Pod里所有容器);如果Pod所在的Node宕機,就會將這個Node上的所有Pod重新調度到其他節點上。

    細心的讀者是否發現:

    當Pod越來越多,Pod IP 也越來越多,那是如何從茫茫IP地址中找到需要的服務呢?換句話來說,是否有一種提供唯一入口的機制,來完成對Pod的訪問,而無需關心訪問到具體是哪個Pod(who care :happy:)?

    Kubernetes 提供了這種機制,稱之為 Service。

    Service概念

    Service服務是Kubernetes里的核心資源對象之一,從名稱上來看,理解其為一個”服務“也不為過,Service的作用是為相同標識的一系列Pod提供唯一的訪問地址。

    Service使用的唯一地址稱為ClusterIP,僅當在集群內的容器才能通過此IP訪問到Service

    它具體實現對一系列Pod綁定,需要再引入Label的概念,才能做出解答。

    Label概念

    Kubernetes 提供了Label(標籤)來對Pod、Service、RC、Node等進行標記。相當於打標籤,Label可以是一組KV鍵值對,也可以是一個set

    一個資源對象可以定義任意數量的Label,同一個Label可以添加到任意數量的資源對象上。通常由定義資源對象時添加,創建后亦可動態添加或刪除。

    Service如何動態綁定Pods?

    原來,在定義 Pod 時,設置一系列 Label 標籤,Service 創建時定義了 Label Selector(Label Selector 可以類比為對 Label 進行 SQL 的 where 查詢),kube-proxy 進程通過 Service的Label Selector 來選擇對應的 Pod,自動建立每個 Service 到對應 Pod 的請求轉發路由表,從而實現 Service 的智能負載均衡機制。

    小結:Pod是K8s最小的執行單元,包含一個Pause容器與多個業務容器,每個Pod中容器共享Pod IP,容器之間可直接作用Pod IP通信;Label是一種標籤,它將標籤打在Pod上時,Service可以通過定義Label Selector(Label查詢規則)來選擇Pod,具體實現路由表建立的是kube-proxy

    部署應用實踐(Minikube)

    安裝kubectl需要安裝成本地服務,這裡是debian10,更多參考https://developer.aliyun.com/mirror/kubernetes

    sudo su -
    apt-get update && apt-get install -y apt-transport-https
    curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - 
    cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
    deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
    EOF  
    apt-get update
    apt-get install -y kubelet kubectl
    exit
    

    下載安裝Minikube(阿里雲修改版):

    curl -Lo minikube-linux-amd64-1.11.0-aliyuncs http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v1.11.0/minikube-linux-amd64
    sudo install minikube-linux-amd64-1.11.0-aliyuncs /usr/local/bin/minikube
    

    使用魔改版是因為官方代碼里有些地方寫死了地址,而國內無法訪問

    部署k8s集群:

    minikube start --driver docker --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers --kubernetes-version v1.18.3
    

    本地有docker時使用此driver,其他可選的虛擬化技術參考https://minikube.sigs.k8s.io/docs/drivers/ 選擇

    #部署一個Pod,Pod的deployment是一種默認的無狀態多備份的部署類型
    kubectl create deployment hello-minikube --image=registry.cn-hangzhou.aliyuncs.com/google_containers/echoserver:1.4
    #查看集群中當前的Pod列表
    kubectl get pods
    #創建的NodePort類型Service,將所有Node開放一個端口號,通過這個端口將流量轉發到Service以及下游Pods
    kubectl expose deployment hello-minikube --type=NodePort --port=8080
    #獲取暴露 Service 的 URL 以查看 Service 的詳細信息:
    minikube service hello-minikube --url
    #minikube提供的特色功能,直接通過瀏覽器打開剛才創建的Service的外部訪問地址
    minikube service hello-minikube
    

    自動打開瀏覽器訪問服務(Minikube特色功能)

    提示:這張圖中的request-uri的解釋是不正確的,但是與 <minikube這個唯一Node的IP>:<NodePort><Service的ClusterIP>:<ServicePort>都不是完全匹配的,不大理解這塊提示,有緣人希望可解我所惑,在此先行感謝

    查看Pod的描述信息

    kubectl describe pod hello-minikube
    

    最下方可以清楚得看到K8s集群default-scheduler成功指派我們要求的Pod在節點minikube上創建,kubelet收到信息后,拉取鏡像並啟動了容器

    部署流程原理簡述

    1. kubectl 發送創建 deployment 類型、名為hello-minikube的Pod 請求到 kube-apiserver
    2. kube-apiserver 將這條描述信息存到 etcd
    3. kube-scheduler 監控 etcd 得到描述信息,監測Node負載情況,創建Pod部署描述信息到etcd
    4. 待部署Node的kubelet監聽到 etcd 中有自己的部署Pod信息,取出信息並按圖拉取業務鏡像,創建pause容器與業務容器(此時集群外部無法訪問)
    5. kubectl 執行expose命令,同時將創建Service與NodePort信息存到etcd中
    6. 各節點kube-proxy監聽到etcd變化,創建邏輯上的Service與路由信息,開闢NodePort
    7. 當請求到達<任意Node節點IP>:<NodePort> 時,根據路由錶轉發到指定的Service上,負載均衡到Pod,提供服務

    集群外部訪問:

    參考

    • https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/
    • https://kubernetes.io/docs/concepts/services-networking/
    • https://minikube.sigs.k8s.io/docs/start/
    • https://minikube.sigs.k8s.io/docs/handbook/controls/
    • 《Kubernetes權威指南》第 4 版

    行文過程中難免出現錯誤,還請讀者評論幫忙改正,大家共同進步,在此感謝

    轉載請註明出處,文章中概念引入《Kubernetes權威指南》很多,侵權改。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    FB行銷專家,教你從零開始的技巧

  • async/await剖析

    async/await剖析

    JavaScript是單線程的,為了避免同步阻塞可能會帶來的一些負面影響,引入了異步非阻塞機制,而對於異步執行的解決方案從最早的回調函數,到ES6Promise對象以及Generator函數,每次都有所改進,但是卻又美中不足,他們都有額外的複雜性,都需要理解抽象的底層運行機制,直到在ES7中引入了async/await,他可以簡化使用多個Promise時的同步行為,在編程的時候甚至都不需要關心這個操作是否為異步操作。

    分析

    首先使用async/await執行一組異步操作,並不需要回調嵌套也不需要寫多個then方法,在使用上甚至覺得這本身就是一個同步操作,當然在正式使用上應該將await語句放置於 try...catch代碼塊中,因為await命令後面的Promise對象,運行結果可能是rejected

    function promise(){
        return new Promise((resolve, reject) => {
           var rand = Math.random() * 2; 
           setTimeout(() => resolve(rand), 1000);
        });
    }
    
    async function asyncFunct(){
        var r1 = await promise();
        console.log(1, r1);
        var r2 = await promise();
        console.log(2, r2);
        var r3 = await promise();
        console.log(3, r3);
    }
    
    asyncFunct();
    

    async/await實際上是Generator函數的語法糖,如Promises類似於結構化回調,async/await在實現上結合了Generator函數與Promise函數,下面使用Generator函數加Thunk函數的形式實現一個與上邊相同的例子,可以看到只是將async替換成了*放置在函數右端,並將await替換成了yield,所以說async/await實際上是Generator函數的語法糖,此處唯一不同的地方在於實現了一個流程的自動管理函數run,而async/await內置了執行器,關於這個例子的實現下邊會詳述。對比來看,asyncawait,比起*yield,語義更清楚,async表示函數里有異步操作,await表示緊跟在後面的表達式需要等待結果。

    function thunkFunct(index){
        return function f(funct){
            var rand = Math.random() * 2;
            setTimeout(() => funct(rand), 1000)
        }
    }
    
    function* generator(){ 
        var r1 = yield thunkFunct();
        console.log(1, r1);
        var r2 = yield thunkFunct();
        console.log(2, r2);
        var r3 = yield thunkFunct();
        console.log(3, r3);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            // console.log(res.value);
            res.value(next);
        }
    
        next();
    }
    
    run(generator);
    

    實現

    async函數內置了執行器,能夠實現函數執行的自動流程管理,通過Generator yield ThunkGenerator yield Promise實現一個自動流程管理,只需要編寫Generator函數以及Thunk函數或者Promise對象並傳入自執行函數,就可以實現類似於async/await的效果。

    Generator yield Thunk

    自動流程管理run函數,首先需要知道在調用next()方法時,如果傳入了參數,那麼這個參數會傳給上一條執行的yield語句左邊的變量,在這個函數中,第一次執行next時並未傳遞參數,而且在第一個yield上邊也並不存在接收變量的語句,無需傳遞參數,接下來就是判斷是否執行完這個生成器函數,在這裏並沒有執行完,那麼將自定義的next函數傳入res.value中,這裏需要注意res.value是一個函數,可以在下邊的例子中將註釋的那一行執行,然後就可以看到這個值是f(funct){...},此時我們將自定義的next函數傳遞后,就將next的執行權限交予了f這個函數,在這個函數執行完異步任務后,會執行回調函數,在這個回調函數中會觸發生成器的下一個next方法,並且這個next方法是傳遞了參數的,上文提到傳入參數後會將其傳遞給上一條執行的yield語句左邊的變量,那麼在這一次執行中會將這個參數值傳遞給r1,然後在繼續執行next,不斷往複,直到生成器函數結束運行,這樣就實現了流程的自動管理。

    function thunkFunct(index){
        return function f(funct){
            var rand = Math.random() * 2;
            setTimeout(() => funct(rand), 1000)
        }
    }
    
    function* generator(){ 
        var r1 = yield thunkFunct();
        console.log(1, r1);
        var r2 = yield thunkFunct();
        console.log(2, r2);
        var r3 = yield thunkFunct();
        console.log(3, r3);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            // console.log(res.value);
            res.value(next);
        }
    
        next();
    }
    
    run(generator);
    

    Generator yield Promise

    相對於使用Thunk函數來做流程自動管理,使用Promise來實現相對更加簡單,Promise實例能夠知道上一次回調什麼時候執行,通過then方法啟動下一個yield,不斷繼續執行,這樣就實現了流程的自動管理。

    function promise(){
        return new Promise((resolve,reject) => {
            var rand = Math.random() * 2;
            setTimeout( () => resolve(rand), 1000);
        })
    }
    
    function* generator(){ 
        var r1 = yield promise();
        console.log(1, r1);
        var r2 = yield promise();
        console.log(2, r2);
        var r3 = yield promise();
        console.log(3, r3);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            res.value.then(data => next(data));
        }
    
        next();
    }
    
    run(generator);
    
    // 比較完整的流程自動管理函數
    function promise(){
        return new Promise((resolve,reject) => {
            var rand = Math.random() * 2;
            setTimeout( () => resolve(rand), 1000);
        })
    }
    
    function* generator(){ 
        var r1 = yield promise();
        console.log(1, r1);
        var r2 = yield promise();
        console.log(2, r2);
        var r3 = yield promise();
        console.log(3, r3);
    }
    
    function run(generator){
        return new Promise((resolve, reject) => {
            var g = generator();
            
            var next = function(data){
                var res = null;
                try{
                    res = g.next(data);
                }catch(e){
                    return reject(e);
                }
                if(!res) return reject(null);
                if(res.done) return resolve(res.value);
                Promise.resolve(res.value).then(data => {
                    next(data);
                },(e) => {
                    throw new Error(e);
                });
            }
            
            next();
        })
       
    }
    
    run(generator).then( () => {
        console.log("Finish");
    });
    

    每日一題

    https://github.com/WindrunnerMax/EveryDay
    

    參考

    https://segmentfault.com/a/1190000007535316
    http://www.ruanyifeng.com/blog/2015/05/co.html
    http://www.ruanyifeng.com/blog/2015/05/async.html
    

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※超省錢租車方案

    ※教你寫出一流的銷售文案?

    網頁設計最專業,超強功能平台可客製化

  • Tensorflow 中(批量)讀取數據的案列分析及TFRecord文件的打包與讀取

    Tensorflow 中(批量)讀取數據的案列分析及TFRecord文件的打包與讀取

    內容概要:

    單一數據讀取方式:

      第一種:slice_input_producer()

    # 返回值可以直接通過 Session.run([images, labels])查看,且第一個參數必須放在列表中,如[...]
    [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)

      第二種:string_input_producer()

    # 需要定義文件讀取器,然後通過讀取器中的 read()方法來獲取數據(返回值類型 key,value),再通過 Session.run(value)查看
    file_queue = tf.train.string_input_producer(filename, num_epochs=None, shuffle=True)

    reader = tf.WholeFileReader() # 定義文件讀取器
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容

      !!!num_epochs=None,不指定迭代次數,這樣文件隊列中元素個數也不限定(None*數據集大小)。

      !!!如果不是None,則此函數創建本地計數器 epochs,需要使用local_variables_initializer()初始化局部變量

      !!!以上兩種方法都可以生成文件名隊列。

    (隨機)批量數據讀取方式:

    batchsize=2  # 每次讀取的樣本數量
    tf.train.batch(tensors, batch_size=batchsize)
    tf.train.shuffle_batch(tensors, batch_size=batchsize, capacity=batchsize*10, min_after_dequeue=batchsize*5) # capacity > min_after_dequeue

      !!!以上所有讀取數據的方法,在Session.run()之前必須開啟文件隊列線程 tf.train.start_queue_runners()

     TFRecord文件的打包與讀取

     

     一、單一數據讀取方式

    第一種:slice_input_producer()

    def slice_input_producer(tensor_list, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None)

    案例1:

    import tensorflow as tf
    
    images = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg']
    labels = [1, 2, 3, 4]
    
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    
    # 當num_epochs=2時,此時文件隊列中只有 2*4=8個樣本,所有在取第9個樣本時會出錯
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=2, shuffle=True)
    
    data = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    print(type(data))   # <class 'list'>
    
    with tf.Session() as sess:
        # sess.run(tf.local_variables_initializer())
        sess.run(tf.local_variables_initializer())
        coord = tf.train.Coordinator()  # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)  # 開始在圖表中收集隊列運行器
    
        for i in range(10):
            print(sess.run(data))
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image3.jpg', 3]
    """

      !!!slice_input_producer() 中的第一個參數需要放在一個列表中,列表中的每個元素可以是 List 或 Tensor,如 [images,labels],

      !!!num_epochs設置

     

     第二種:string_input_producer()

    def string_input_producer(string_tensor, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None, cancel_op=None)

    文件讀取器

      不同類型的文件對應不同的文件讀取器,我們稱為 reader對象

      該對象的 read 方法自動讀取文件,並創建數據隊列,輸出key/文件名,value/文件內容;

    reader = tf.TextLineReader()      ### 一行一行讀取,適用於所有文本文件

    reader = tf.TFRecordReader() ### A Reader that outputs the records from a TFRecords file
    reader = tf.WholeFileReader() ### 一次讀取整個文件,適用圖片

     案例2:讀取csv文件

    iimport tensorflow as tf
    
    filename = ['data/A.csv', 'data/B.csv', 'data/C.csv']
    
    file_queue = tf.train.string_input_producer(filename, shuffle=True, num_epochs=2)   # 生成文件名隊列
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    # reader = tf.TextLineReader()            # 定義文件讀取器(一行一行的讀)
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容
    print(type(file_queue))
    
    init = [tf.global_variables_initializer(), tf.local_variables_initializer()]
    with tf.Session() as sess:
        sess.run(init)
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        try:
            while not coord.should_stop():
                for i in range(6):
                    print(sess.run([key, value]))
                break
        except tf.errors.OutOfRangeError:
            print('read done')
        finally:
            coord.request_stop()
        coord.join(threads)
    
    """
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    運行結果:
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    """
    """
    reader = tf.TextLineReader()           # 定義文件讀取器(一行一行的讀)
    運行結果:
    [b'data/B.csv:1', b'4.jpg,4']
    [b'data/B.csv:2', b'5.jpg,5']
    [b'data/B.csv:3', b'6.jpg,6']
    [b'data/C.csv:1', b'7.jpg,7']
    [b'data/C.csv:2', b'8.jpg,8']
    [b'data/C.csv:3', b'9.jpg,9']
    """

    案例3:讀取圖片(每次讀取全部圖片內容,不是一行一行)

    import tensorflow as tf
    
    filename = ['1.jpg', '2.jpg']
    filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=1)
    reader = tf.WholeFileReader()              # 文件讀取器
    key, value = reader.read(filename_queue)   # 讀取文件 key:文件名;value:圖片數據,bytes
    
    with tf.Session() as sess:
        tf.local_variables_initializer().run()
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)
    
        for i in range(filename.__len__()):
            image_data = sess.run(value)
            with open('img_%d.jpg' % i, 'wb') as f:
                f.write(image_data)
        coord.request_stop()
        coord.join(threads)

     

     二、(隨機)批量數據讀取方式:

      功能:shuffle_batch() 和 batch() 這兩個API都是從文件隊列中批量獲取數據,使用方式類似;

    案例4:slice_input_producer() 與 batch()

    import tensorflow as tf
    import numpy as np
    
    images = np.arange(20).reshape([10, 2])
    label = np.asarray(range(0, 10))
    images = tf.cast(images, tf.float32)  # 可以註釋掉,不影響運行結果
    label = tf.cast(label, tf.int32)     # 可以註釋掉,不影響運行結果
    
    batchsize = 6   # 每次獲取元素的數量
    input_queue = tf.train.slice_input_producer([images, label], num_epochs=None, shuffle=False)
    image_batch, label_batch = tf.train.batch(input_queue, batch_size=batchsize)
    
    # 隨機獲取 batchsize個元素,其中,capacity:隊列容量,這個參數一定要比 min_after_dequeue 大
    # image_batch, label_batch = tf.train.shuffle_batch(input_queue, batch_size=batchsize, capacity=64, min_after_dequeue=10)
    
    with tf.Session() as sess:
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
        for cnt in range(2):
            print("第{}次獲取數據,每次batch={}...".format(cnt+1, batchsize))
            image_batch_v, label_batch_v = sess.run([image_batch, label_batch])
            print(image_batch_v, label_batch_v, label_batch_v.__len__())
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    第1次獲取數據,每次batch=6...
    [[ 0.  1.]
     [ 2.  3.]
     [ 4.  5.]
     [ 6.  7.]
     [ 8.  9.]
     [10. 11.]] [0 1 2 3 4 5] 6
    第2次獲取數據,每次batch=6...
    [[12. 13.]
     [14. 15.]
     [16. 17.]
     [18. 19.]
     [ 0.  1.]
     [ 2.  3.]] [6 7 8 9 0 1] 6
    """

     案例5:從本地批量的讀取圖片 — string_input_producer() 與 batch()

     1 import tensorflow as tf
     2 import glob
     3 import cv2 as cv
     4 
     5 def read_imgs(filename, picture_format, input_image_shape, batch_size=1):
     6     """
     7     從本地批量的讀取圖片
     8     :param filename: 圖片路徑(包括圖片的文件名),[]
     9     :param picture_format: 圖片的格式,如 bmp,jpg,png等; string
    10     :param input_image_shape: 輸入圖像的大小; (h,w,c)或[]
    11     :param batch_size: 每次從文件隊列中加載圖片的數量; int
    12     :return: batch_size張圖片數據, Tensor
    13     """
    14     global new_img
    15     # 創建文件隊列
    16     file_queue = tf.train.string_input_producer(filename, num_epochs=1, shuffle=True)
    17     # 創建文件讀取器
    18     reader = tf.WholeFileReader()
    19     # 讀取文件隊列中的文件
    20     _, img_bytes = reader.read(file_queue)
    21     # print(img_bytes)    # Tensor("ReaderReadV2_19:1", shape=(), dtype=string)
    22     # 對圖片進行解碼
    23     if picture_format == ".bmp":
    24         new_img = tf.image.decode_bmp(img_bytes, channels=1)
    25     elif picture_format == ".jpg":
    26         new_img = tf.image.decode_jpeg(img_bytes, channels=3)
    27     else:
    28         pass
    29     # 重新設置圖片的大小
    30     # new_img = tf.image.resize_images(new_img, input_image_shape)
    31     new_img = tf.reshape(new_img, input_image_shape)
    32     # 設置圖片的數據類型
    33     new_img = tf.image.convert_image_dtype(new_img, tf.uint8)
    34 
    35     # return new_img
    36     return tf.train.batch([new_img], batch_size)
    37 
    38 
    39 def main():
    40     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    41     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    42     print(type(image_batch))
    43     # image_path = glob.glob(r'.\*.jpg')
    44     # image_batch = read_imgs(image_path, ".jpg", (313, 500, 3), 1)
    45 
    46     sess = tf.Session()
    47     sess.run(tf.local_variables_initializer())
    48     tf.train.start_queue_runners(sess=sess)
    49 
    50     image_batch = sess.run(image_batch)
    51     print(type(image_batch))    # <class 'numpy.ndarray'>
    52 
    53     for i in range(image_batch.__len__()):
    54         cv.imshow("win_"+str(i), image_batch[i])
    55     cv.waitKey()
    56     cv.destroyAllWindows()
    57 
    58 def start():
    59     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    60     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    61     print(type(image_batch))    # <class 'tensorflow.python.framework.ops.Tensor'>
    62 
    63 
    64     with tf.Session() as sess:
    65         sess.run(tf.local_variables_initializer())
    66         coord = tf.train.Coordinator()      # 線程的協調器
    67         threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
    68         image_batch = sess.run(image_batch)
    69         print(type(image_batch))    # <class 'numpy.ndarray'>
    70 
    71         for i in range(image_batch.__len__()):
    72             cv.imshow("win_"+str(i), image_batch[i])
    73         cv.waitKey()
    74         cv.destroyAllWindows()
    75 
    76         # 若使用 with 方式打開 Session,且沒加如下2行語句,則會出錯
    77         # ERROR:tensorflow:Exception in QueueRunner: Enqueue operation was cancelled;
    78         # 原因:文件隊列線程還處於工作狀態(隊列中還有圖片數據),而加載完batch_size張圖片會話就會自動關閉,同時關閉文件隊列線程
    79         coord.request_stop()
    80         coord.join(threads)
    81 
    82 
    83 if __name__ == "__main__":
    84     # main()
    85     start()

    從本地批量的讀取圖片案例

     

    案列6:TFRecord文件打包與讀取

     1 def write_TFRecord(filename, data, labels, is_shuffler=True):
     2     """
     3     將數據打包成TFRecord格式
     4     :param filename: 打包後路徑名,默認在工程目錄下創建該文件;String
     5     :param data: 需要打包的文件路徑名;list
     6     :param labels: 對應文件的標籤;list
     7     :param is_shuffler:是否隨機初始化打包后的數據,默認:True;Bool
     8     :return: None
     9     """
    10     im_data = list(data)
    11     im_labels = list(labels)
    12 
    13     index = [i for i in range(im_data.__len__())]
    14     if is_shuffler:
    15         np.random.shuffle(index)
    16 
    17     # 創建寫入器,然後使用該對象寫入樣本example
    18     writer = tf.python_io.TFRecordWriter(filename)
    19     for i in range(im_data.__len__()):
    20         im_d = im_data[index[i]]    # im_d:存放着第index[i]張圖片的路徑信息
    21         im_l = im_labels[index[i]]  # im_l:存放着對應圖片的標籤信息
    22 
    23         # # 獲取當前的圖片數據  方式一:
    24         # data = cv2.imread(im_d)
    25         # # 創建樣本
    26         # ex = tf.train.Example(
    27         #     features=tf.train.Features(
    28         #         feature={
    29         #             "image": tf.train.Feature(
    30         #                 bytes_list=tf.train.BytesList(
    31         #                     value=[data.tobytes()])), # 需要打包成bytes類型
    32         #             "label": tf.train.Feature(
    33         #                 int64_list=tf.train.Int64List(
    34         #                     value=[im_l])),
    35         #         }
    36         #     )
    37         # )
    38         # 獲取當前的圖片數據  方式二:相對於方式一,打包文件佔用空間小了一半多
    39         data = tf.gfile.FastGFile(im_d, "rb").read()
    40         ex = tf.train.Example(
    41             features=tf.train.Features(
    42                 feature={
    43                     "image": tf.train.Feature(
    44                         bytes_list=tf.train.BytesList(
    45                             value=[data])), # 此時的data已經是bytes類型
    46                     "label": tf.train.Feature(
    47                         int64_list=tf.train.Int64List(
    48                             value=[im_l])),
    49                 }
    50             )
    51         )
    52 
    53         # 寫入將序列化之後的樣本
    54         writer.write(ex.SerializeToString())
    55     # 關閉寫入器
    56     writer.close()

    TFRecord文件打包案列

     

     1 import tensorflow as tf
     2 import cv2
     3 
     4 def read_TFRecord(file_list, batch_size=10):
     5     """
     6     讀取TFRecord文件
     7     :param file_list: 存放TFRecord的文件名,List
     8     :param batch_size: 每次讀取圖片的數量
     9     :return: 解析後圖片及對應的標籤
    10     """
    11     file_queue = tf.train.string_input_producer(file_list, num_epochs=None, shuffle=True)
    12     reader = tf.TFRecordReader()
    13     _, ex = reader.read(file_queue)
    14     batch = tf.train.shuffle_batch([ex], batch_size, capacity=batch_size * 10, min_after_dequeue=batch_size * 5)
    15 
    16     feature = {
    17         'image': tf.FixedLenFeature([], tf.string),
    18         'label': tf.FixedLenFeature([], tf.int64)
    19     }
    20     example = tf.parse_example(batch, features=feature)
    21 
    22     images = tf.decode_raw(example['image'], tf.uint8)
    23     images = tf.reshape(images, [-1, 32, 32, 3])
    24 
    25     return images, example['label']
    26 
    27 
    28 
    29 def main():
    30     # filelist = ['data/train.tfrecord']
    31     filelist = ['data/test.tfrecord']
    32     images, labels = read_TFRecord(filelist, 2)
    33     with tf.Session() as sess:
    34         sess.run(tf.local_variables_initializer())
    35         coord = tf.train.Coordinator()
    36         threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    37 
    38         try:
    39             while not coord.should_stop():
    40                 for i in range(1):
    41                     image_bth, _ = sess.run([images, labels])
    42                     print(_)
    43 
    44                     cv2.imshow("image_0", image_bth[0])
    45                     cv2.imshow("image_1", image_bth[1])
    46                 break
    47         except tf.errors.OutOfRangeError:
    48             print('read done')
    49         finally:
    50             coord.request_stop()
    51         coord.join(threads)
    52         cv2.waitKey(0)
    53         cv2.destroyAllWindows()
    54 
    55 if __name__ == "__main__":
    56     main()

    TFReord文件的讀取案列

     

    ,

    內容概要:

    單一數據讀取方式:

      第一種:slice_input_producer()

    # 返回值可以直接通過 Session.run([images, labels])查看,且第一個參數必須放在列表中,如[...]
    [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)

      第二種:string_input_producer()

    # 需要定義文件讀取器,然後通過讀取器中的 read()方法來獲取數據(返回值類型 key,value),再通過 Session.run(value)查看
    file_queue = tf.train.string_input_producer(filename, num_epochs=None, shuffle=True)

    reader = tf.WholeFileReader() # 定義文件讀取器
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容

      !!!num_epochs=None,不指定迭代次數,這樣文件隊列中元素個數也不限定(None*數據集大小)。

      !!!如果不是None,則此函數創建本地計數器 epochs,需要使用local_variables_initializer()初始化局部變量

      !!!以上兩種方法都可以生成文件名隊列。

    (隨機)批量數據讀取方式:

    batchsize=2  # 每次讀取的樣本數量
    tf.train.batch(tensors, batch_size=batchsize)
    tf.train.shuffle_batch(tensors, batch_size=batchsize, capacity=batchsize*10, min_after_dequeue=batchsize*5) # capacity > min_after_dequeue

      !!!以上所有讀取數據的方法,在Session.run()之前必須開啟文件隊列線程 tf.train.start_queue_runners()

     TFRecord文件的打包與讀取

     

     一、單一數據讀取方式

    第一種:slice_input_producer()

    def slice_input_producer(tensor_list, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None)

    案例1:

    import tensorflow as tf
    
    images = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg']
    labels = [1, 2, 3, 4]
    
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    
    # 當num_epochs=2時,此時文件隊列中只有 2*4=8個樣本,所有在取第9個樣本時會出錯
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=2, shuffle=True)
    
    data = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    print(type(data))   # <class 'list'>
    
    with tf.Session() as sess:
        # sess.run(tf.local_variables_initializer())
        sess.run(tf.local_variables_initializer())
        coord = tf.train.Coordinator()  # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)  # 開始在圖表中收集隊列運行器
    
        for i in range(10):
            print(sess.run(data))
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image3.jpg', 3]
    """

      !!!slice_input_producer() 中的第一個參數需要放在一個列表中,列表中的每個元素可以是 List 或 Tensor,如 [images,labels],

      !!!num_epochs設置

     

     第二種:string_input_producer()

    def string_input_producer(string_tensor, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None, cancel_op=None)

    文件讀取器

      不同類型的文件對應不同的文件讀取器,我們稱為 reader對象

      該對象的 read 方法自動讀取文件,並創建數據隊列,輸出key/文件名,value/文件內容;

    reader = tf.TextLineReader()      ### 一行一行讀取,適用於所有文本文件

    reader = tf.TFRecordReader() ### A Reader that outputs the records from a TFRecords file
    reader = tf.WholeFileReader() ### 一次讀取整個文件,適用圖片

     案例2:讀取csv文件

    iimport tensorflow as tf
    
    filename = ['data/A.csv', 'data/B.csv', 'data/C.csv']
    
    file_queue = tf.train.string_input_producer(filename, shuffle=True, num_epochs=2)   # 生成文件名隊列
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    # reader = tf.TextLineReader()            # 定義文件讀取器(一行一行的讀)
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容
    print(type(file_queue))
    
    init = [tf.global_variables_initializer(), tf.local_variables_initializer()]
    with tf.Session() as sess:
        sess.run(init)
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        try:
            while not coord.should_stop():
                for i in range(6):
                    print(sess.run([key, value]))
                break
        except tf.errors.OutOfRangeError:
            print('read done')
        finally:
            coord.request_stop()
        coord.join(threads)
    
    """
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    運行結果:
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    """
    """
    reader = tf.TextLineReader()           # 定義文件讀取器(一行一行的讀)
    運行結果:
    [b'data/B.csv:1', b'4.jpg,4']
    [b'data/B.csv:2', b'5.jpg,5']
    [b'data/B.csv:3', b'6.jpg,6']
    [b'data/C.csv:1', b'7.jpg,7']
    [b'data/C.csv:2', b'8.jpg,8']
    [b'data/C.csv:3', b'9.jpg,9']
    """

    案例3:讀取圖片(每次讀取全部圖片內容,不是一行一行)

    import tensorflow as tf
    
    filename = ['1.jpg', '2.jpg']
    filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=1)
    reader = tf.WholeFileReader()              # 文件讀取器
    key, value = reader.read(filename_queue)   # 讀取文件 key:文件名;value:圖片數據,bytes
    
    with tf.Session() as sess:
        tf.local_variables_initializer().run()
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)
    
        for i in range(filename.__len__()):
            image_data = sess.run(value)
            with open('img_%d.jpg' % i, 'wb') as f:
                f.write(image_data)
        coord.request_stop()
        coord.join(threads)

     

     二、(隨機)批量數據讀取方式:

      功能:shuffle_batch() 和 batch() 這兩個API都是從文件隊列中批量獲取數據,使用方式類似;

    案例4:slice_input_producer() 與 batch()

    import tensorflow as tf
    import numpy as np
    
    images = np.arange(20).reshape([10, 2])
    label = np.asarray(range(0, 10))
    images = tf.cast(images, tf.float32)  # 可以註釋掉,不影響運行結果
    label = tf.cast(label, tf.int32)     # 可以註釋掉,不影響運行結果
    
    batchsize = 6   # 每次獲取元素的數量
    input_queue = tf.train.slice_input_producer([images, label], num_epochs=None, shuffle=False)
    image_batch, label_batch = tf.train.batch(input_queue, batch_size=batchsize)
    
    # 隨機獲取 batchsize個元素,其中,capacity:隊列容量,這個參數一定要比 min_after_dequeue 大
    # image_batch, label_batch = tf.train.shuffle_batch(input_queue, batch_size=batchsize, capacity=64, min_after_dequeue=10)
    
    with tf.Session() as sess:
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
        for cnt in range(2):
            print("第{}次獲取數據,每次batch={}...".format(cnt+1, batchsize))
            image_batch_v, label_batch_v = sess.run([image_batch, label_batch])
            print(image_batch_v, label_batch_v, label_batch_v.__len__())
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    第1次獲取數據,每次batch=6...
    [[ 0.  1.]
     [ 2.  3.]
     [ 4.  5.]
     [ 6.  7.]
     [ 8.  9.]
     [10. 11.]] [0 1 2 3 4 5] 6
    第2次獲取數據,每次batch=6...
    [[12. 13.]
     [14. 15.]
     [16. 17.]
     [18. 19.]
     [ 0.  1.]
     [ 2.  3.]] [6 7 8 9 0 1] 6
    """

     案例5:從本地批量的讀取圖片 — string_input_producer() 與 batch()

     1 import tensorflow as tf
     2 import glob
     3 import cv2 as cv
     4 
     5 def read_imgs(filename, picture_format, input_image_shape, batch_size=1):
     6     """
     7     從本地批量的讀取圖片
     8     :param filename: 圖片路徑(包括圖片的文件名),[]
     9     :param picture_format: 圖片的格式,如 bmp,jpg,png等; string
    10     :param input_image_shape: 輸入圖像的大小; (h,w,c)或[]
    11     :param batch_size: 每次從文件隊列中加載圖片的數量; int
    12     :return: batch_size張圖片數據, Tensor
    13     """
    14     global new_img
    15     # 創建文件隊列
    16     file_queue = tf.train.string_input_producer(filename, num_epochs=1, shuffle=True)
    17     # 創建文件讀取器
    18     reader = tf.WholeFileReader()
    19     # 讀取文件隊列中的文件
    20     _, img_bytes = reader.read(file_queue)
    21     # print(img_bytes)    # Tensor("ReaderReadV2_19:1", shape=(), dtype=string)
    22     # 對圖片進行解碼
    23     if picture_format == ".bmp":
    24         new_img = tf.image.decode_bmp(img_bytes, channels=1)
    25     elif picture_format == ".jpg":
    26         new_img = tf.image.decode_jpeg(img_bytes, channels=3)
    27     else:
    28         pass
    29     # 重新設置圖片的大小
    30     # new_img = tf.image.resize_images(new_img, input_image_shape)
    31     new_img = tf.reshape(new_img, input_image_shape)
    32     # 設置圖片的數據類型
    33     new_img = tf.image.convert_image_dtype(new_img, tf.uint8)
    34 
    35     # return new_img
    36     return tf.train.batch([new_img], batch_size)
    37 
    38 
    39 def main():
    40     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    41     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    42     print(type(image_batch))
    43     # image_path = glob.glob(r'.\*.jpg')
    44     # image_batch = read_imgs(image_path, ".jpg", (313, 500, 3), 1)
    45 
    46     sess = tf.Session()
    47     sess.run(tf.local_variables_initializer())
    48     tf.train.start_queue_runners(sess=sess)
    49 
    50     image_batch = sess.run(image_batch)
    51     print(type(image_batch))    # <class 'numpy.ndarray'>
    52 
    53     for i in range(image_batch.__len__()):
    54         cv.imshow("win_"+str(i), image_batch[i])
    55     cv.waitKey()
    56     cv.destroyAllWindows()
    57 
    58 def start():
    59     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    60     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    61     print(type(image_batch))    # <class 'tensorflow.python.framework.ops.Tensor'>
    62 
    63 
    64     with tf.Session() as sess:
    65         sess.run(tf.local_variables_initializer())
    66         coord = tf.train.Coordinator()      # 線程的協調器
    67         threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
    68         image_batch = sess.run(image_batch)
    69         print(type(image_batch))    # <class 'numpy.ndarray'>
    70 
    71         for i in range(image_batch.__len__()):
    72             cv.imshow("win_"+str(i), image_batch[i])
    73         cv.waitKey()
    74         cv.destroyAllWindows()
    75 
    76         # 若使用 with 方式打開 Session,且沒加如下2行語句,則會出錯
    77         # ERROR:tensorflow:Exception in QueueRunner: Enqueue operation was cancelled;
    78         # 原因:文件隊列線程還處於工作狀態(隊列中還有圖片數據),而加載完batch_size張圖片會話就會自動關閉,同時關閉文件隊列線程
    79         coord.request_stop()
    80         coord.join(threads)
    81 
    82 
    83 if __name__ == "__main__":
    84     # main()
    85     start()

    從本地批量的讀取圖片案例

     

    案列6:TFRecord文件打包與讀取

     1 def write_TFRecord(filename, data, labels, is_shuffler=True):
     2     """
     3     將數據打包成TFRecord格式
     4     :param filename: 打包後路徑名,默認在工程目錄下創建該文件;String
     5     :param data: 需要打包的文件路徑名;list
     6     :param labels: 對應文件的標籤;list
     7     :param is_shuffler:是否隨機初始化打包后的數據,默認:True;Bool
     8     :return: None
     9     """
    10     im_data = list(data)
    11     im_labels = list(labels)
    12 
    13     index = [i for i in range(im_data.__len__())]
    14     if is_shuffler:
    15         np.random.shuffle(index)
    16 
    17     # 創建寫入器,然後使用該對象寫入樣本example
    18     writer = tf.python_io.TFRecordWriter(filename)
    19     for i in range(im_data.__len__()):
    20         im_d = im_data[index[i]]    # im_d:存放着第index[i]張圖片的路徑信息
    21         im_l = im_labels[index[i]]  # im_l:存放着對應圖片的標籤信息
    22 
    23         # # 獲取當前的圖片數據  方式一:
    24         # data = cv2.imread(im_d)
    25         # # 創建樣本
    26         # ex = tf.train.Example(
    27         #     features=tf.train.Features(
    28         #         feature={
    29         #             "image": tf.train.Feature(
    30         #                 bytes_list=tf.train.BytesList(
    31         #                     value=[data.tobytes()])), # 需要打包成bytes類型
    32         #             "label": tf.train.Feature(
    33         #                 int64_list=tf.train.Int64List(
    34         #                     value=[im_l])),
    35         #         }
    36         #     )
    37         # )
    38         # 獲取當前的圖片數據  方式二:相對於方式一,打包文件佔用空間小了一半多
    39         data = tf.gfile.FastGFile(im_d, "rb").read()
    40         ex = tf.train.Example(
    41             features=tf.train.Features(
    42                 feature={
    43                     "image": tf.train.Feature(
    44                         bytes_list=tf.train.BytesList(
    45                             value=[data])), # 此時的data已經是bytes類型
    46                     "label": tf.train.Feature(
    47                         int64_list=tf.train.Int64List(
    48                             value=[im_l])),
    49                 }
    50             )
    51         )
    52 
    53         # 寫入將序列化之後的樣本
    54         writer.write(ex.SerializeToString())
    55     # 關閉寫入器
    56     writer.close()

    TFRecord文件打包案列

     

     1 import tensorflow as tf
     2 import cv2
     3 
     4 def read_TFRecord(file_list, batch_size=10):
     5     """
     6     讀取TFRecord文件
     7     :param file_list: 存放TFRecord的文件名,List
     8     :param batch_size: 每次讀取圖片的數量
     9     :return: 解析後圖片及對應的標籤
    10     """
    11     file_queue = tf.train.string_input_producer(file_list, num_epochs=None, shuffle=True)
    12     reader = tf.TFRecordReader()
    13     _, ex = reader.read(file_queue)
    14     batch = tf.train.shuffle_batch([ex], batch_size, capacity=batch_size * 10, min_after_dequeue=batch_size * 5)
    15 
    16     feature = {
    17         'image': tf.FixedLenFeature([], tf.string),
    18         'label': tf.FixedLenFeature([], tf.int64)
    19     }
    20     example = tf.parse_example(batch, features=feature)
    21 
    22     images = tf.decode_raw(example['image'], tf.uint8)
    23     images = tf.reshape(images, [-1, 32, 32, 3])
    24 
    25     return images, example['label']
    26 
    27 
    28 
    29 def main():
    30     # filelist = ['data/train.tfrecord']
    31     filelist = ['data/test.tfrecord']
    32     images, labels = read_TFRecord(filelist, 2)
    33     with tf.Session() as sess:
    34         sess.run(tf.local_variables_initializer())
    35         coord = tf.train.Coordinator()
    36         threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    37 
    38         try:
    39             while not coord.should_stop():
    40                 for i in range(1):
    41                     image_bth, _ = sess.run([images, labels])
    42                     print(_)
    43 
    44                     cv2.imshow("image_0", image_bth[0])
    45                     cv2.imshow("image_1", image_bth[1])
    46                 break
    47         except tf.errors.OutOfRangeError:
    48             print('read done')
    49         finally:
    50             coord.request_stop()
    51         coord.join(threads)
    52         cv2.waitKey(0)
    53         cv2.destroyAllWindows()
    54 
    55 if __name__ == "__main__":
    56     main()

    TFReord文件的讀取案列

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※教你寫出一流的銷售文案?

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 10.DRF-認證

    10.DRF-認證

    Django rest framework源碼分析(1)—-認證

    一、基礎

    1.1.安裝

    兩種方式:

    • github
    • pip直接安裝
    pip install djangorestframework
    

    1.2.需要先了解的一些知識

    理解下面兩個知識點非常重要,django-rest-framework源碼中到處都是基於CBV和面向對象的封裝

    (1)面向對象封裝的兩大特性

    把同一類方法封裝到類中
    
    將數據封裝到對象中
    

    (2)CBV

    基於反射實現根據請求方式不同,執行不同的方法

    原理:url–>view方法–>dispatch方法(反射執行其它方法:GET/POST/PUT/DELETE等等)

    二、簡單實例

    2.1.settings

    先創建一個project和一個app(我這裏命名為API)

    首先要在settings的app中添加

    INSTALLED_APPS = [
        'rest_framework',
    ]
    

    2.2.url

    from django.contrib import admin
    from django.urls import path
    from API.views import AuthView
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/v1/auth/',AuthView.as_view()),
    ]
    

    2.3.models

    一個保存用戶的信息

    一個保存用戶登錄成功后的token

    from django.db import models
    
    class UserInfo(models.Model):
        USER_TYPE = (
            (1,'普通用戶'),
            (2,'VIP'),
            (3,'SVIP')
        )
    
        user_type = models.IntegerField(choices=USER_TYPE)
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
    
    class UserToken(models.Model):
        user = models.OneToOneField(UserInfo,on_delete=models.CASCADE)
        token = models.CharField(max_length=64)
    

    2.4.views

    用戶登錄(返回token並保存到數據庫)

    from django.shortcuts import render
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from API import models
    
    def md5(user):
        import hashlib
        import time
        #當前時間,相當於生成一個隨機的字符串
        ctime = str(time.time())
        m = hashlib.md5(bytes(user,encoding='utf-8'))
        m.update(bytes(ctime,encoding='utf-8'))
        return m.hexdigest()
    
    class AuthView(object):
        def post(self,request,*args,**kwargs):
            ret = {'code':1000,'msg':None}
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:
                    ret['code'] = 1001
                    ret['msg'] = '用戶名或密碼錯誤'
                #為用戶創建token
                token = md5(user)
                #存在就更新,不存在就創建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '請求異常'
            return JsonResponse(ret)
    

    2.5.利用postman發請求

    如果用戶名和密碼正確的話 會生成token值,下次該用戶再登錄時,token的值就會更新

    數據庫中可以看到token的值

    當用戶名或密碼錯誤時,拋出異常

    三、添加認證

    基於上面的例子,添加一個認證的類

    3.1.url

    path('api/v1/order/',OrderView.as_view()),
    

    3.2.views

    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from API import models
    from rest_framework.request import Request
    from rest_framework import exceptions
    from rest_framework.authentication import BasicAuthentication
    
    ORDER_DICT = {
        1:{
            'name':'apple',
            'price':15
        },
        2:{
            'name':'dog',
            'price':100
        }
    }
    
    def md5(user):
        import hashlib
        import time
        #當前時間,相當於生成一個隨機的字符串
        ctime = str(time.time())
        m = hashlib.md5(bytes(user,encoding='utf-8'))
        m.update(bytes(ctime,encoding='utf-8'))
        return m.hexdigest()
    
    class AuthView(object):
        '''用於用戶登錄驗證'''
        def post(self,request,*args,**kwargs):
            ret = {'code':1000,'msg':None}
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:
                    ret['code'] = 1001
                    ret['msg'] = '用戶名或密碼錯誤'
                #為用戶創建token
                token = md5(user)
                #存在就更新,不存在就創建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '請求異常'
            return JsonResponse(ret)
    
    
    class Authentication(APIView):
        '''認證'''
        def authenticate(self,request):
            token = request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用戶認證失敗')
            #在rest framework內部會將這兩個字段賦值給request,以供後續操作使用
            return (token_obj.user,token_obj)
    
        def authenticate_header(self, request):
            pass
    
    class OrderView(APIView):
        '''訂單相關業務'''
    
        authentication_classes = [Authentication,]    #添加認證
        def get(self,request,*args,**kwargs):
            #request.user
            #request.auth
            ret = {'code':1000,'msg':None,'data':None}
            try:
                ret['data'] = ORDER_DICT
            except Exception as e:
                pass
            return JsonResponse(ret)
    

    3.3用postman發get請求

    請求的時候沒有帶token,可以看到會显示“用戶認證失敗”

    這樣就達到了認證的效果,django-rest-framework的認證是怎麼實現的呢,下面基於這個例子來剖析drf的源碼。

    四、drf的認證源碼分析

    源碼流程圖

    請求先到dispatch

    dispatch()主要做了兩件事

    • 封裝request
    • 認證  

    具體看我寫的代碼裏面的註釋

    def dispatch(self, request, *args, **kwargs):
    	"""
    	`.dispatch()` is pretty much the same as Django's regular dispatch,
    	but with extra hooks for startup, finalize, and exception handling.
    	 """
    	self.args = args
        self.kwargs = kwargs
        #對原始request進行加工,豐富了一些功能
        #Request(
        #     request,
        #     parsers=self.get_parsers(),
        #     authenticators=self.get_authenticators(),
        #     negotiator=self.get_content_negotiator(),
        #     parser_context=parser_context
        # )
        #request(原始request,[BasicAuthentications對象,])
        #獲取原生request,request._request
        #獲取認證類的對象,request.authticators
        #1.封裝request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    
        try:
            #2.認證
            self.initial(request, *args, **kwargs)
    
            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
    
            response = handler(request, *args, **kwargs)
    
    	except Exception as exc:
            response = self.handle_exception(exc)
    
    	self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    

    4.1.reuqest

    (1)initialize_request()

    可以看到initialize()就是封裝原始request

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)
    
        return Request(
            request,
            parsers=self.get_parsers(),
            #[BasicAuthentication(),],把對象封裝到request裏面了
            authenticators=self.get_authenticators(),    
            negotiator=self.get_content_negotiator(), parser_context=parser_context )
    

    (2)get_authenticators()

    通過列表生成式,返回對象的列表

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes]
    

    (3)authentication_classes

    APIView裏面有個 authentication_classes 字段

    可以看到默認是去全局的配置文件找(api_settings)

    class APIView(View):
    
        # The following policies may be set at either globally, or per-view.
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
        parser_classes = api_settings.DEFAULT_PARSER_CLASSES
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
        permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
        metadata_class = api_settings.DEFAULT_METADATA_CLASS
        versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    

    4.2.認證

    self.initial(request, *args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        #對原始request進行加工,豐富了一些功能
        #Request(
        #     request,
        #     parsers=self.get_parsers(),
        #     authenticators=self.get_authenticators(),
        #     negotiator=self.get_content_negotiator(),
        #     parser_context=parser_context
        # )
        #request(原始request,[BasicAuthentications對象,])
        #獲取原生request,request._request
        #獲取認證類的對象,request.authticators
        #1.封裝request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    
        try:
            #2.認證
            self.initial(request, *args, **kwargs)
    
            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
    
            response = handler(request, *args, **kwargs)
    
    	except Exception as exc:
            response = self.handle_exception(exc)
    
    	self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    

    (1)initial()

    主要看 self.perform_authentication(request),實現認證

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)
    
        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
    
        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
    
        # Ensure that the incoming request is permitted
        #3.實現認證
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
    

    (2)perform_authentication()

    調用了request.user

    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.
    
    	Note that if you override this and simply 'pass', then authentication
    	will instead be performed lazily, the first time either
    	`request.user` or `request.auth` is accessed.
    	"""
        request.user
    

    (3)user

    request.user的request的位置

    點進去可以看到Request有個user方法,加 @property 表示調用user方法的時候不需要加括號“user()”,可以直接調用:request.user

    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                #獲取認證對象,進行一步步的認證
                self._authenticate()
        return self._user
    

    (4)_authenticate()

    循環所有authenticator對象

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        #循環認證類的所有對象
        #執行對象的authenticate方法
        for authenticator in self.authenticators:
            try:
                #執行認證類的authenticate方法
                #這裏分三種情況
                #1.如果authenticate方法拋出異常,self._not_authenticated()執行
                #2.有返回值,必須是元組:(request.user,request.auth)
                #3.返回None,表示當前認證不處理,等下一個認證來處理
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
    
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return
    
    	self._not_authenticated()
    

    返回值就是例子中的:

    token_obj.user-->>request.user
    token_obj-->>request.auth
    #在rest framework內部會將這兩個字段賦值給request,以供後續操作使用
    return (token_obj.user,token_obj)     #例子中的return
    

    當都沒有返回值,就執行self._not_authenticated(),相當於匿名用戶,沒有通過認證

    def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.
    
    	Defaults are None, AnonymousUser & None.
    	"""
        self._authenticator = None
    
        if api_settings.UNAUTHENTICATED_USER:
            self.user = api_settings.UNAUTHENTICATED_USER()   #AnonymousUser匿名用戶
        else:
            self.user = None
    
    	if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()  #None
    	else:
            self.auth = None
    

    面向對象知識:

    子類繼承 父類,調用方法的時候:

    • 優先去自己裏面找有沒有這個方法,有就執行自己的
    • 只有當自己裏面沒有這個方法的時候才會去父類找

    因為authenticate方法我們自己寫,所以當執行authenticate()的時候就是執行我們自己寫的認證

    父類中的authenticate方法

    def authenticate(self, request):
        return (self.force_user, self.force_token)
    

    我們自己寫的

    class Authentication(APIView):
        '''用於用戶登錄驗證'''
        def authenticate(self,request):
            token = request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用戶認證失敗')
            #在rest framework內部會將這兩個字段賦值給request,以供後續操作使用
            return (token_obj.user,token_obj)
    

    認證的流程就是上面寫的,弄懂了原理,再寫代碼就更容易理解為什麼了。

    4.3.配置文件

    繼續解讀源碼

    默認是去全局配置文件中找,所以我們應該在settings.py中配置好路徑

    api_settings源碼

    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    
    def reload_api_settings(*args, **kwargs):
        setting = kwargs['setting']
        if setting == 'REST_FRAMEWORK':
            api_settings.reload()
    

    setting中‘REST_FRAMEWORK’中找

    全局配置方法:

    API文件夾下面新建文件夾utils,再新建auth.py文件,裏面寫上認證的類

    settings.py

    #設置全局認證
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',]   #裏面寫你的認證的類的路徑
    }
    

    auth.py

    # API/utils/auth.py
    
    from rest_framework import exceptions
    from API import models
    
    
    class Authentication(object):
        '''用於用戶登錄驗證'''
        def authenticate(self,request):
            token = request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用戶認證失敗')
            #在rest framework內部會將這兩個字段賦值給request,以供後續操作使用
            return (token_obj.user,token_obj)
    
        def authenticate_header(self, request):
            pass
    

    在settings裏面設置的全局認證,所有業務都需要經過認證,如果想讓某個不需要認證,只需要在其中添加下面的代碼:

    authentication_classes = []    #裏面為空,代表不需要認證
    
    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from API import models
    from rest_framework.request import Request
    from rest_framework import exceptions
    from rest_framework.authentication import BasicAuthentication
    
    ORDER_DICT = {
        1:{
            'name':'apple',
            'price':15
        },
        2:{
            'name':'dog',
            'price':100
        }
    }
    
    def md5(user):
        import hashlib
        import time
        #當前時間,相當於生成一個隨機的字符串
        ctime = str(time.time())
        m = hashlib.md5(bytes(user,encoding='utf-8'))
        m.update(bytes(ctime,encoding='utf-8'))
        return m.hexdigest()
    
    class AuthView(APIView):
        '''用於用戶登錄驗證'''
    
        authentication_classes = []    #裏面為空,代表不需要認證
    
        def post(self,request,*args,**kwargs):
            ret = {'code':1000,'msg':None}
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:
                    ret['code'] = 1001
                    ret['msg'] = '用戶名或密碼錯誤'
                #為用戶創建token
                token = md5(user)
                #存在就更新,不存在就創建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '請求異常'
            return JsonResponse(ret)
    
    
    
    
    class OrderView(APIView):
        '''訂單相關業務'''
    
    
        def get(self,request,*args,**kwargs):
            # self.dispatch
            #request.user
            #request.auth
            ret = {'code':1000,'msg':None,'data':None}
            try:
                ret['data'] = ORDER_DICT
            except Exception as e:
                pass
            return JsonResponse(ret)
    
    API/view.py代碼
    

    再測試一下我們的代碼

    不帶token發請求

    帶token發請求

    五、drf的內置認證

    rest_framework裏面內置了一些認證,我們自己寫的認證類都要繼承內置認證類 “BaseAuthentication”

    4.1.BaseAuthentication源碼:

    class BaseAuthentication(object):
        """
        All authentication classes should extend BaseAuthentication.
        """
    
        def authenticate(self, request):
            """
            Authenticate the request and return a two-tuple of (user, token).
            """
            #內置的認證類,authenticate方法,如果不自己寫,默認則拋出異常
            raise NotImplementedError(".authenticate() must be overridden.")
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            #authenticate_header方法,作用是當認證失敗的時候,返回的響應頭
            pass
    

    4.2.修改自己寫的認證類

    自己寫的Authentication必須繼承內置認證類BaseAuthentication

    # API/utils/auth/py
    
    from rest_framework import exceptions
    from API import models
    from rest_framework.authentication import BaseAuthentication
    
    
    class Authentication(BaseAuthentication):
        '''用於用戶登錄驗證'''
        def authenticate(self,request):
            token = request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用戶認證失敗')
            #在rest framework內部會將這兩個字段賦值給request,以供後續操作使用
            return (token_obj.user,token_obj)
    
        def authenticate_header(self, request):
            pass
    

    4.3.其它內置認證類

    rest_framework裏面還內置了其它認證類,我們主要用到的就是BaseAuthentication,剩下的很少用到

    六、總結

    自己寫認證類方法梳理

    (1)創建認證類

    • 繼承BaseAuthentication —>>1.重寫authenticate方法;2.authenticate_header方法直接寫pass就可以(這個方法必須寫)

    (2)authenticate()返回值(三種)

    • None —–>>>當前認證不管,等下一個認證來執行
    • raise exceptions.AuthenticationFailed(‘用戶認證失敗’) # from rest_framework import exceptions
    • 有返回值元祖形式:(元素1,元素2) #元素1複製給request.user; 元素2複製給request.auth

    (3)局部使用

    • authentication_classes = [BaseAuthentication,]

    (4)全局使用

    #設置全局認證
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',]
    }
    

    源碼流程

    —>>dispatch

        –封裝request

           —獲取定義的認證類(全局/局部),通過列表生成式創建對象 

         —initial

           —-peform_authentication

             —–request.user (每部循環創建的對象)

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

    FB行銷專家,教你從零開始的技巧

  • SpringMVC之文件上傳

    SpringMVC之文件上傳

    一、環境搭建

    1、新建項目

    (1)在” main”目錄下新建” java”與” resources”目錄

    (2)將” java”設置為”Sources Root”

    (3)將” resources”設置為”Resources Root”

    (4)在”java”目錄下新建”StudyProject.Controller”包

    (5)在”StudyProject.Controller”包下新建”TestController”類

    (6)在”resources”目錄下新建”Spring.xml”

    (7)在”WEB-INF”目錄下新建文件夾”Pages”

    (8)在“Pages”目錄下新建”success.jsp”

    2、整體框架

    3、TestController類和success.jsp

    (1)TestController類

    原代碼

    1 package StudyProject.Controller;
    2 
    3 public class TestController {
    4 }

    編寫前端控制器及路徑

    修改后

    1 package StudyProject.Controller;
    2 
    3 import org.springframework.stereotype.Controller;
    4 import org.springframework.web.bind.annotation.RequestMapping;
    5 
    6 @Controller
    7 @RequestMapping(path="/testController")
    8 public class TestController {
    9 }

    (2)success.jsp

    原代碼

    1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    2 <html>
    3 <head>
    4     <title>Title</title>
    5 </head>
    6 <body>
    7 
    8 </body>
    9 </html>

    添加一個跳轉成功提示

    修改后

     1 <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
     2 <html>
     3 <head>
     4     <title>Title</title>
     5 </head>
     6 <body>
     7 
     8     <h3>跳轉成功</h3>
     9 
    10 </body>
    11 </html>

    4、配置文件

    (1)pom.xml

    原代碼

    1   <properties>
    2     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    3     <maven.compiler.source>1.7</maven.compiler.source>
    4     <maven.compiler.target>1.7</maven.compiler.target>
    5   </properties>

    修改版本,並且加上spring.version

    修改后

    1   <properties>
    2     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    3     <maven.compiler.source>14.0.1</maven.compiler.source>
    4     <maven.compiler.target>14.0.1</maven.compiler.target>
    5     <spring.version>5.0.2.RELEASE</spring.version>
    6   </properties>

    原代碼

    1   <dependencies>
    2     <dependency>
    3       <groupId>junit</groupId>
    4       <artifactId>junit</artifactId>
    5       <version>4.11</version>
    6       <scope>test</scope>
    7     </dependency>
    8   </dependencies>

    在<dependencies></dependency>里加入坐標依賴,原有的可以刪去

    修改后

     1   <!-- 導入坐標依賴 -->
     2   <dependencies>
     3     <dependency>
     4       <groupId>org.springframework</groupId>
     5       <artifactId>spring-context</artifactId>
     6       <version>${spring.version}</version>
     7     </dependency>
     8     <dependency>
     9       <groupId>org.springframework</groupId>
    10       <artifactId>spring-web</artifactId>
    11       <version>${spring.version}</version>
    12     </dependency>
    13     <dependency>
    14       <groupId>org.springframework</groupId>
    15       <artifactId>spring-webmvc</artifactId>
    16       <version>${spring.version}</version>
    17     </dependency>
    18     <dependency>
    19       <groupId>javax.servlet</groupId>
    20       <artifactId>servlet-api</artifactId>
    21       <version>2.5</version>
    22       <scope>provided</scope>
    23     </dependency>
    24     <dependency>
    25       <groupId>javax.servlet.jsp</groupId>
    26       <artifactId>jsp-api</artifactId>
    27       <version>2.0</version>
    28       <scope>provided</scope>
    29     </dependency>
    30   </dependencies>

    (2)web.xml

    原代碼

    1 <!DOCTYPE web-app PUBLIC
    2  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    3  "http://java.sun.com/dtd/web-app_2_3.dtd" >
    4 
    5 <web-app>
    6   <display-name>Archetype Created Web Application</display-name>
    7 </web-app>

    配置前段控制器與解決中文亂碼的過濾器

    修改后

     1 <!DOCTYPE web-app PUBLIC
     2  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     3  "http://java.sun.com/dtd/web-app_2_3.dtd" >
     4 
     5 <web-app>
     6   <display-name>Archetype Created Web Application</display-name>
     7 
     8   <!--配置解決中文亂碼的過濾器-->
     9   <filter>
    10     <filter-name>characterEncodingFilter</filter-name>
    11     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    12     <init-param>
    13       <param-name>encoding</param-name>
    14       <param-value>UTF-8</param-value>
    15     </init-param>
    16   </filter>
    17   <filter-mapping>
    18   <filter-name>characterEncodingFilter</filter-name>
    19   <url-pattern>/*</url-pattern>
    20   </filter-mapping>
    21 
    22   <!-- 配置前端控制器 -->
    23   <servlet>
    24     <servlet-name>dispatcherServlet</servlet-name>
    25     <!-- 創建前端控制器DispatcherServlet對象 -->
    26     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    27     <!-- 使前端控制器初始化時讀取Spring.xml文件創建Spring核心容器 -->
    28     <init-param>
    29       <param-name>contextConfigLocation</param-name>
    30       <param-value>classpath*:Spring.xml</param-value>
    31     </init-param>
    32     <!-- 設置該Servlet的優先級別為最高,使之最早創建(在應用啟動時就加載並初始化這個servlet -->
    33     <load-on-startup>1</load-on-startup>
    34   </servlet>
    35   <servlet-mapping>
    36     <servlet-name>dispatcherServlet</servlet-name>
    37     <url-pattern>/</url-pattern>
    38   </servlet-mapping>
    39 
    40 </web-app>

    (3)Spring.xml

    原代碼

    1 <?xml version="1.0" encoding="UTF-8"?>
    2 <beans xmlns="http://www.springframework.org/schema/beans"
    3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    5 
    6 </beans>

    配置spring創建容器時掃描的包、視圖解析器、開啟spring註解支持等

    修改后

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:mvc="http://www.springframework.org/schema/mvc"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     6        xsi:schemaLocation="
     7         http://www.springframework.org/schema/beans
     8         http://www.springframework.org/schema/beans/spring-beans.xsd
     9         http://www.springframework.org/schema/mvc
    10         http://www.springframework.org/schema/mvc/spring-mvc.xsd
    11         http://www.springframework.org/schema/context
    12         http://www.springframework.org/schema/context/spring-context.xsd">
    13 
    14     <!-- 配置spring創建容器時掃描的包 -->
    15     <context:component-scan base-package="StudyProject.Controller"></context:component-scan>
    16 
    17     <!-- 配置視圖解析器,用於解析項目跳轉到的文件的位置 -->
    18     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    19         <!-- 尋找包的路徑 -->
    20         <property name="prefix" value="/WEB-INF/pages/"></property>
    21         <!-- 尋找文件的後綴名 -->
    22         <property name="suffix" value=".jsp"></property>
    23     </bean>
    24 
    25     <!-- 配置spring開啟註解mvc的支持 -->
    26     <mvc:annotation-driven></mvc:annotation-driven>
    27 </beans>

    5、Tomcat服務器(本地已建SpringMVC項目,Spring_MVC項目僅做示範)

    點擊”Add Configurations”配置Tomcat服務器

    二、文件上傳

    1、傳統方式上傳文件

    (1)TestController類

    在控制器內部新增”testMethod_Traditional”方法

     1 @Controller
     2 @RequestMapping(path="/testController")
     3 public class TestController {
     4 
     5     @RequestMapping(path="/testMethod_Traditional")
     6     public String testMethod_Traditional(HttpServletRequest request) throws Exception {
     7         System.out.println("執行了testMethod_Traditional方法");
     8 
     9         //獲取文件上傳目錄
    10         String path = request.getSession().getServletContext().getRealPath("/uploads");
    11         //創建file對象
    12         File file = new File(path);
    13         //判斷路徑是否存在,若不存在,創建該路徑
    14         if (!file.exists()) {
    15             file.mkdir();
    16         }
    17 
    18         //創建磁盤文件項工廠
    19         DiskFileItemFactory factory = new DiskFileItemFactory();
    20         ServletFileUpload fileUpload = new ServletFileUpload(factory);
    21         //解析request對象
    22         List<FileItem> list = fileUpload.parseRequest(request);
    23         //遍歷
    24         for (FileItem fileItem:list) {
    25             // 判斷文件項是普通字段,還是上傳的文件
    26             if (fileItem.isFormField()) {
    27                 //普通字段
    28             } else {
    29                 //上傳文件項
    30                 //獲取上傳文件項的名稱
    31                 String filename = fileItem.getName();
    32                 String uuid = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
    33                 filename = uuid+"_"+filename;
    34                 //上傳文件
    35                 fileItem.write(new File(file,filename));
    36                 //刪除臨時文件
    37                 fileItem.delete();
    38             }
    39         }
    40 
    41         System.out.println("上傳路徑:"+path);
    42         System.out.println("上傳成功");
    43         return "success";
    44     }
    45 
    46 }

    (2)index.jsp

    添加form表單

    1     <form action="testController/testMethod_Traditional" method="post" enctype="multipart/form-data">
    2         圖片 <input type="file" name="uploadfile_Traditional"> <br>
    3         <input type="submit" value="傳統方式上傳文件">
    4     </form>

    (3)結果演示

    點擊服務器”SpringMVC”右側的運行按鈕

    選擇文件然後進行上傳

    點擊上傳按鈕后,執行成功,跳到”success.jsp”界面显示跳轉成功

    在IDEA輸出台查看文件路徑

    按照路徑查看文件是否上傳成功

    2、SpringMVC方式上傳文件

    (1)pom.xml添加文件上傳坐標依賴

    在pom.xml文件<dependencies></dependencies>內添加文件上傳坐標依賴

     1     <!-- 文件上傳 -->
     2     <dependency>
     3       <groupId>commons-fileupload</groupId>
     4       <artifactId>commons-fileupload</artifactId>
     5       <version>1.3.1</version>
     6     </dependency>
     7     <dependency>
     8       <groupId>commons-io</groupId>
     9       <artifactId>commons-io</artifactId>
    10       <version>2.4</version>
    11     </dependency>

    (2)Spring.xml配置文件解析器對象

    在Spring.xml文件<beans></beans>內配置文件解析器對象

    1     <!-- 配置文件解析器對象 -->
    2     <bean id="multipartResolver"
    3           class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    4         <property name="defaultEncoding" value="utf-8"></property>
    5         <property name="maxUploadSize" value="10485760"></property>
    6     </bean>

    (3)TestController類

    在控制器內部新增”testMethod_SpringMVC”方法

     1 @Controller
     2 @RequestMapping(path="/testController")
     3 public class TestController {
     4 
     5     @RequestMapping(path="/testMethod_SpringMVC")
     6     public String testMethod_SpringMVC(HttpServletRequest request,MultipartFile uploadfile_SpringMVC) throws Exception {
     7         System.out.println("執行了testMethod_SpringMVC方法");
     8 
     9         //獲取文件上傳目錄
    10         String path = request.getSession().getServletContext().getRealPath("/uploads");
    11         //創建file對象
    12         File file = new File(path);
    13         //判斷路徑是否存在,若不存在,創建該路徑
    14         if (!file.exists()) {
    15             file.mkdir();
    16         }
    17 
    18         //獲取到上傳文件的名稱
    19         String filename = uploadfile_SpringMVC.getOriginalFilename();
    20         String uuid = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
    21         filename = uuid+"_"+filename;
    22         //上傳文件
    23         uploadfile_SpringMVC.transferTo(new File(file,filename));
    24 
    25         System.out.println("上傳路徑:"+path);
    26         System.out.println("上傳成功");
    27         return "success";
    28     }
    29 
    30 }

    (4)index.jsp

    添加form表單

    1     <form action="testController/testMethod_SpringMVC" method="post" enctype="multipart/form-data">
    2         圖片 <input type="file" name="uploadfile_SpringMVC"> <br>
    3         <input type="submit" value="SpringMVC上傳文件">
    4     </form>

    (5)結果演示

    點擊服務器”SpringMVC”右側的運行按鈕

    選擇文件然後進行上傳

    點擊上傳按鈕后,執行成功,跳到”success.jsp”界面显示跳轉成功

    在IDEA輸出台查看文件路徑

    按照路徑查看文件是否上傳成功

    3、跨服務器上傳文件

    (1)新建”FileuploadServer”項目(過程不再演示)

    不需要建立”java””resources”等文件夾,只需要”index.jsp”显示界面即可

    “index.jsp”代碼

    1 <html>
    2 <body>
    3 <h2>Hello! FileuploadServer</h2>
    4 </body>
    5 </html>

    (2)配置服務器

    點擊”Edit Configurations”配置Tomcat服務器

    為與”SpringMVC”服務器區分,修改”HTTP port”為”9090”,修改”JMX port”為”1090”

    (3)pom.xml添加跨服務器文件上傳坐標依賴

     1     <!-- 跨服務器文件上傳 -->
     2     <dependency>
     3       <groupId>com.sun.jersey</groupId>
     4       <artifactId>jersey-core</artifactId>
     5       <version>1.18.1</version>
     6     </dependency>
     7     <dependency>
     8       <groupId>com.sun.jersey</groupId>
     9       <artifactId>jersey-client</artifactId>
    10       <version>1.18.1</version>
    11     </dependency>

    (4)TestController類

    在控制器內部新增”testMethod_AcrossServer”方法

     1 @Controller
     2 @RequestMapping(path="/testController")
     3 public class TestController {
     4 
     5     @RequestMapping(path="/testMethod_AcrossServer")
     6     public String testMethod_AcrossServer(MultipartFile uploadfile_AcrossServer) throws Exception {
     7         System.out.println("執行了testMethod_AcrossServer方法");
     8 
     9         //定義上傳文件服務器路徑
    10         String path = "http://localhost:9090/FileuploadServer_war_exploded/uploads/";
    11 
    12         //獲取到上傳文件的名稱
    13         String filename = uploadfile_AcrossServer.getOriginalFilename();
    14         String uuid = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
    15         filename = uuid+"_"+filename;
    16 
    17         //創建客戶端對象
    18         Client client = Client.create();
    19         //連接圖片服務器
    20         WebResource webResourcer = client.resource(path+filename);
    21         //向圖片服務器上傳文件
    22         webResourcer.put(uploadfile_AcrossServer.getBytes());
    23 
    24         System.out.println("上傳路徑:"+path);
    25         System.out.println("上傳成功");
    26         return "success";
    27     }
    28 
    29 }

    (5)index.jsp

    添加form表單

    1     <form action="testController/testMethod_AcrossServer" method="post" enctype="multipart/form-data">
    2         圖片 <input type="file" name="uploadfile_AcrossServer"> <br>
    3         <input type="submit" value="跨服務器上傳文件">
    4     </form>

    (6)結果演示

    運行”FileuploadServer”服務器

    運行”SpringMVC”服務器

    在”FileuploadServer”項目的”target/FileuploadServer/”目錄下新建文件夾”uploads”

    選擇文件並進行上傳,上傳成功跳轉到”success.jsp”

    查看IDEA輸出信息

    此時路徑應為”FileuploadServer/target/FileuploadServer/uploads”,在路徑下查看文件是否上傳成功

    三、注意事項

    1、傳統方式上傳文件

    傳統方式上傳時不需要在Spring.xml內配置文件解析器對象使用該方法時需要註釋掉該對象,否則會造成運行成功但上傳文件為空。

    1     <!-- 配置文件解析器對象 -->
    2     <bean id="multipartResolver"
    3           class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    4         <property name="defaultEncoding" value="utf-8"></property>
    5         <property name="maxUploadSize" value="10485760"></property>
    6     </bean>

    即使用傳統方式上傳文件時,應當註釋掉該段代碼

    2、跨服務器上傳文件

    (1)需要修改Tomcat服務器的web.xml配置文件的權限,增加可以寫入的權限,否則會出現405的錯誤。如我所下載的Tomcat-9.0.36的web.xml路徑為”apache-tomcat-9.0.36/conf/web.xml”

    此時IEDA輸出為

    web.xml文件修改處原內容

    應修改為

    修改后的代碼

     1     <servlet>
     2         <servlet-name>default</servlet-name>
     3         <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
     4         <init-param>
     5             <param-name>debug</param-name>
     6             <param-value>0</param-value>
     7         </init-param>
     8         <init-param>
     9             <param-name>listings</param-name>
    10             <param-value>false</param-value>
    11         </init-param>
    12         <init-param>
    13             <param-name>readonly</param-name>
    14             <param-value>false</param-value>
    15         </init-param>
    16         <load-on-startup>1</load-on-startup>
    17     </servlet>

    (2)在跨服務器上傳文件時,需要在”FileuploadServer”項目的”target/FileuploadServer/”目錄下新建文件夾”uploads”,否則會出現409的錯誤

    四、完整代碼

    1、pom.xml(SpringMVC)

      1 <?xml version="1.0" encoding="UTF-8"?>
      2 
      3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      5   <modelVersion>4.0.0</modelVersion>
      6 
      7   <groupId>org.example</groupId>
      8   <artifactId>SpringMVC</artifactId>
      9   <version>1.0-SNAPSHOT</version>
     10   <packaging>war</packaging>
     11 
     12   <name>SpringMVC Maven Webapp</name>
     13   <!-- FIXME change it to the project's website -->
     14   <url>http://www.example.com</url>
     15 
     16   <properties>
     17     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     18     <maven.compiler.source>14.0.1</maven.compiler.source>
     19     <maven.compiler.target>14.0.1</maven.compiler.target>
     20     <spring.version>5.0.2.RELEASE</spring.version>
     21   </properties>
     22 
     23   <!-- 導入坐標依賴 -->
     24   <dependencies>
     25     <dependency>
     26       <groupId>org.springframework</groupId>
     27       <artifactId>spring-context</artifactId>
     28       <version>${spring.version}</version>
     29     </dependency>
     30     <dependency>
     31       <groupId>org.springframework</groupId>
     32       <artifactId>spring-web</artifactId>
     33       <version>${spring.version}</version>
     34     </dependency>
     35     <dependency>
     36       <groupId>org.springframework</groupId>
     37       <artifactId>spring-webmvc</artifactId>
     38       <version>${spring.version}</version>
     39     </dependency>
     40     <dependency>
     41       <groupId>javax.servlet</groupId>
     42       <artifactId>servlet-api</artifactId>
     43       <version>2.5</version>
     44       <scope>provided</scope>
     45     </dependency>
     46     <dependency>
     47       <groupId>javax.servlet.jsp</groupId>
     48       <artifactId>jsp-api</artifactId>
     49       <version>2.0</version>
     50       <scope>provided</scope>
     51     </dependency>
     52 
     53     <!-- 文件上傳(採用傳統方式上傳時需註釋掉該部分) -->
     54     <dependency>
     55       <groupId>commons-fileupload</groupId>
     56       <artifactId>commons-fileupload</artifactId>
     57       <version>1.3.1</version>
     58     </dependency>
     59     <dependency>
     60       <groupId>commons-io</groupId>
     61       <artifactId>commons-io</artifactId>
     62       <version>2.4</version>
     63     </dependency>
     64 
     65     <!-- 跨服務器文件上傳 -->
     66     <dependency>
     67       <groupId>com.sun.jersey</groupId>
     68       <artifactId>jersey-core</artifactId>
     69       <version>1.18.1</version>
     70     </dependency>
     71     <dependency>
     72       <groupId>com.sun.jersey</groupId>
     73       <artifactId>jersey-client</artifactId>
     74       <version>1.18.1</version>
     75     </dependency>
     76 
     77   </dependencies>
     78 
     79   <build>
     80     <finalName>SpringMVC</finalName>
     81     <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
     82       <plugins>
     83         <plugin>
     84           <artifactId>maven-clean-plugin</artifactId>
     85           <version>3.1.0</version>
     86         </plugin>
     87         <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
     88         <plugin>
     89           <artifactId>maven-resources-plugin</artifactId>
     90           <version>3.0.2</version>
     91         </plugin>
     92         <plugin>
     93           <artifactId>maven-compiler-plugin</artifactId>
     94           <version>3.8.0</version>
     95         </plugin>
     96         <plugin>
     97           <artifactId>maven-surefire-plugin</artifactId>
     98           <version>2.22.1</version>
     99         </plugin>
    100         <plugin>
    101           <artifactId>maven-war-plugin</artifactId>
    102           <version>3.2.2</version>
    103         </plugin>
    104         <plugin>
    105           <artifactId>maven-install-plugin</artifactId>
    106           <version>2.5.2</version>
    107         </plugin>
    108         <plugin>
    109           <artifactId>maven-deploy-plugin</artifactId>
    110           <version>2.8.2</version>
    111         </plugin>
    112       </plugins>
    113     </pluginManagement>
    114   </build>
    115 </project>

    2、web.xml(SpringMVC)

     1 <!DOCTYPE web-app PUBLIC
     2  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     3  "http://java.sun.com/dtd/web-app_2_3.dtd" >
     4 
     5 <web-app>
     6   <display-name>Archetype Created Web Application</display-name>
     7 
     8   <!--配置解決中文亂碼的過濾器-->
     9   <filter>
    10     <filter-name>characterEncodingFilter</filter-name>
    11     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    12     <init-param>
    13       <param-name>encoding</param-name>
    14       <param-value>UTF-8</param-value>
    15     </init-param>
    16   </filter>
    17   <filter-mapping>
    18   <filter-name>characterEncodingFilter</filter-name>
    19   <url-pattern>/*</url-pattern>
    20   </filter-mapping>
    21 
    22   <!-- 配置前端控制器 -->
    23   <servlet>
    24     <servlet-name>dispatcherServlet</servlet-name>
    25     <!-- 創建前端控制器DispatcherServlet對象 -->
    26     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    27     <!-- 使前端控制器初始化時讀取Spring.xml文件創建Spring核心容器 -->
    28     <init-param>
    29       <param-name>contextConfigLocation</param-name>
    30       <param-value>classpath*:Spring.xml</param-value>
    31     </init-param>
    32     <!-- 設置該Servlet的優先級別為最高,使之最早創建(在應用啟動時就加載並初始化這個servlet -->
    33     <load-on-startup>1</load-on-startup>
    34   </servlet>
    35   <servlet-mapping>
    36     <servlet-name>dispatcherServlet</servlet-name>
    37     <url-pattern>/</url-pattern>
    38   </servlet-mapping>
    39 
    40 </web-app>

    3、Spring.xml(SpringMVC)

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:mvc="http://www.springframework.org/schema/mvc"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     6        xsi:schemaLocation="
     7         http://www.springframework.org/schema/beans
     8         http://www.springframework.org/schema/beans/spring-beans.xsd
     9         http://www.springframework.org/schema/mvc
    10         http://www.springframework.org/schema/mvc/spring-mvc.xsd
    11         http://www.springframework.org/schema/context
    12         http://www.springframework.org/schema/context/spring-context.xsd">
    13 
    14     <!-- 配置spring創建容器時掃描的包 -->
    15     <context:component-scan base-package="StudyProject.Controller"></context:component-scan>
    16 
    17     <!-- 配置視圖解析器,用於解析項目跳轉到的文件的位置 -->
    18     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    19         <!-- 尋找包的路徑 -->
    20         <property name="prefix" value="/WEB-INF/pages/"></property>
    21         <!-- 尋找文件的後綴名 -->
    22         <property name="suffix" value=".jsp"></property>
    23     </bean>
    24 
    25     <!-- 配置文件解析器對象 -->
    26     <bean id="multipartResolver"
    27           class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    28         <property name="defaultEncoding" value="utf-8"></property>
    29         <property name="maxUploadSize" value="10485760"></property>
    30     </bean>
    31     
    32     <!-- 配置spring開啟註解mvc的支持 -->
    33     <mvc:annotation-driven></mvc:annotation-driven>
    34 </beans>

    4、TestController類(SpringMVC)

      1 package StudyProject.Controller;
      2 
      3 import com.sun.jersey.api.client.Client;
      4 import com.sun.jersey.api.client.WebResource;
      5 import org.apache.commons.fileupload.FileItem;
      6 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
      7 import org.apache.commons.fileupload.servlet.ServletFileUpload;
      8 import org.springframework.stereotype.Controller;
      9 import org.springframework.web.bind.annotation.RequestMapping;
     10 import org.springframework.web.multipart.MultipartFile;
     11 import javax.servlet.http.HttpServletRequest;
     12 import java.io.File;
     13 import java.util.List;
     14 import java.util.UUID;
     15 
     16 @Controller
     17 @RequestMapping(path="/testController")
     18 public class TestController {
     19 
     20     @RequestMapping(path="/testMethod_Traditional")
     21     public String testMethod_Traditional(HttpServletRequest request) throws Exception {
     22         System.out.println("執行了testMethod_Traditional方法");
     23 
     24         //獲取文件上傳目錄
     25         String path = request.getSession().getServletContext().getRealPath("/uploads");
     26         //創建file對象
     27         File file = new File(path);
     28         //判斷路徑是否存在,若不存在,創建該路徑
     29         if (!file.exists()) {
     30             file.mkdir();
     31         }
     32 
     33         //創建磁盤文件項工廠
     34         DiskFileItemFactory factory = new DiskFileItemFactory();
     35         ServletFileUpload fileUpload = new ServletFileUpload(factory);
     36         //解析request對象
     37         List<FileItem> list = fileUpload.parseRequest(request);
     38         //遍歷
     39         for (FileItem fileItem:list) {
     40             // 判斷文件項是普通字段,還是上傳的文件
     41             if (fileItem.isFormField()) {
     42                 //普通字段
     43             } else {
     44                 //上傳文件項
     45                 //獲取上傳文件項的名稱
     46                 String filename = fileItem.getName();
     47                 String uuid = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
     48                 filename = uuid+"_"+filename;
     49                 //上傳文件
     50                 fileItem.write(new File(file,filename));
     51                 //刪除臨時文件
     52                 fileItem.delete();
     53             }
     54         }
     55 
     56         System.out.println("上傳路徑:"+path);
     57         System.out.println("上傳成功");
     58         return "success";
     59     }
     60 
     61     @RequestMapping(path="/testMethod_SpringMVC")
     62     public String testMethod_SpringMVC(HttpServletRequest request,MultipartFile uploadfile_SpringMVC) throws Exception {
     63         System.out.println("執行了testMethod_SpringMVC方法");
     64 
     65         //獲取文件上傳目錄
     66         String path = request.getSession().getServletContext().getRealPath("/uploads");
     67         //創建file對象
     68         File file = new File(path);
     69         //判斷路徑是否存在,若不存在,創建該路徑
     70         if (!file.exists()) {
     71             file.mkdir();
     72         }
     73 
     74         //獲取到上傳文件的名稱
     75         String filename = uploadfile_SpringMVC.getOriginalFilename();
     76         String uuid = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
     77         filename = uuid+"_"+filename;
     78         //上傳文件
     79         uploadfile_SpringMVC.transferTo(new File(file,filename));
     80 
     81         System.out.println("上傳路徑:"+path);
     82         System.out.println("上傳成功");
     83         return "success";
     84     }
     85 
     86     @RequestMapping(path="/testMethod_AcrossServer")
     87     public String testMethod_AcrossServer(MultipartFile uploadfile_AcrossServer) throws Exception {
     88         System.out.println("執行了testMethod_AcrossServer方法");
     89 
     90         //定義上傳文件服務器路徑
     91         String path = "http://localhost:9090/FileuploadServer_war_exploded/uploads/";
     92 
     93         //獲取到上傳文件的名稱
     94         String filename = uploadfile_AcrossServer.getOriginalFilename();
     95         String uuid = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
     96         filename = uuid+"_"+filename;
     97 
     98         //創建客戶端對象
     99         Client client = Client.create();
    100         //連接圖片服務器
    101         WebResource webResourcer = client.resource(path+filename);
    102         //向圖片服務器上傳文件
    103         webResourcer.put(uploadfile_AcrossServer.getBytes());
    104 
    105         System.out.println("上傳路徑:"+path);
    106         System.out.println("上傳成功");
    107         return "success";
    108     }
    109 
    110 }

    5、index.jsp(SpringMVC)

     1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     2 <html>
     3 <head>
     4     <title>Title</title>
     5 </head>
     6 <body>
     7 
     8     <form action="testController/testMethod_Traditional" method="post" enctype="multipart/form-data">
     9         圖片 <input type="file" name="uploadfile_Traditional"> <br>
    10         <input type="submit" value="傳統方式上傳文件">
    11     </form>
    12 
    13     <br><br><br>
    14 
    15     <form action="testController/testMethod_SpringMVC" method="post" enctype="multipart/form-data">
    16         圖片 <input type="file" name="uploadfile_SpringMVC"> <br>
    17         <input type="submit" value="SpringMVC上傳文件">
    18     </form>
    19 
    20     <br><br><br>
    21 
    22     <form action="testController/testMethod_AcrossServer" method="post" enctype="multipart/form-data">
    23         圖片 <input type="file" name="uploadfile_AcrossServer"> <br>
    24         <input type="submit" value="跨服務器上傳文件">
    25     </form>
    26 
    27 </body>
    28 </html>

    6、success.jsp(SpringMVC)

     1 <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
     2 <html>
     3 <head>
     4     <title>Title</title>
     5 </head>
     6 <body>
     7 
     8     <h3>跳轉成功</h3>
     9 
    10 </body>
    11 </html>

    7、index.jsp(FileuploadServer)

    1 <html>
    2 <body>
    3 <h2>Hello! FileuploadServer</h2>
    4 </body>
    5 </html>

    學習資料來源:黑馬程序員

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 印度抗霾害,2030年全面改賣電動車

    印度抗霾害,2030年全面改賣電動車

    在川普總統發出豪語,宣布美國將退出「巴黎氣候協議」的同時,印度能源部門卻宣布,為了對抗日益嚴重的空氣污染,預計在2030 年後印度將只賣電動車。

    CNNMoney 報導,做為開發中國家之一,印度的經濟以驚人速度成長中,但在產業與交通持續發展的情況下,嚴重的空氣污染也隨之而來,根據研究估計,空污每年約造成印度120 萬人因喪命,甚至有醫師如此形容,「在首都新德里呼吸,就像是每天抽10 根菸」。

    不僅是空氣污染,蓬勃發展的經濟也讓印度成為全球第三大石油進口國,每年在石油上花費將近1,500 億美元,電動車發展將能使石油需求大幅下降,因此印度政府宣布,在2030 年後,在印度銷售的每輛汽車都必須仰賴電力,而非石油。

    為了達到目標,印度開始推行「全國電動車計畫」(National Electric Mobility Mission Plan),希望至2020 年時,電動車和混合動力車的年銷量能達到600-700 萬輛,能源部長Piyush Goyal 表示,在電動車市場起步階段,政府會透過經費補助來協助成長,但在那之後,車商就得仰賴市場需求去推動產能上升。

    對於電動車大廠特斯拉(Tesla)來說,這當然是非常好的消息,儘管特斯拉還並未進入印度市場,但馬斯克(Elon Musk)也隨即在新聞出現後發布了一條推特,稱讚印度政府對於太陽能、電動車等環保能源產業的支持。

    在馬斯克發文後,印度當地最大的電動車商馬璽達(Mahindra)執行長也在推特表示,歡迎特斯拉這個強力的競爭對手盡快加入,「馬斯克你該來了,你不會希望把整個市場都拱手讓給馬璽達吧?人多才熱鬧,也會更加環保。」

    為了改善空污情況,印度政府一直有在嘗試相關措施,在2016 年1 月時,新德里政府就宣布,男子開車必須「做一休一」,允許開車的日數以車牌尾數的奇偶來決定,單身婦女則每日都允許被開車。

    這項規定對減少空污取得了很大成功,但一但市場轉向純電動車,對於環境將會造成更正面的影響,根據世界經濟論壇報導指出,在採取這項計畫後,至2030 年前,印度將有望將碳排放量減少37%。

    (本文由《報》授權提供。照片來源: shared by CC 2.0)

     

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 寶馬計畫從2013年開始投產i子品牌電動車

    寶馬集團計畫從2013年開始將i子品牌車型投入生產,該品牌為專屬品牌。

    寶馬集團董事長諾伯特·雷瑟夫(Norbert Reithofer)日前表示,公司將集中精力準備i系列新款電動車的生產工作,按照計畫i子品牌將在2013年下半年投放到市場,而投產時間則早於該節點。此前有消息稱該子品牌旗下的i3車型明年將在德國萊比錫(Leipzig)工廠投產,初步產能3萬輛/年。

    2011年2月21日,寶馬正式宣佈發佈i子品牌。根據寶馬注冊商標透露的資訊,車型覆蓋從i1到i9。寶馬官方正式公佈了i3純電動車和i8插電式混動車兩款車型,分別以Megacity Vehicle概念車和Vision EfficientDynamics概念技術為藍本。另外,據悉i1為小型城市電動車,i4為兩座酷派,i5為帶有酷派風格的四門Sedan轎車。i4將在今年11月底洛杉磯車展上亮相。

    此前有傳聞指出,寶馬i專案工程浩大,耗費成本甚巨,導致集團考慮放棄該子品牌。不過寶馬方面否認了該說法。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※別再煩惱如何寫文案,掌握八大原則!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    FB行銷專家,教你從零開始的技巧

  • 雷諾計畫批量投產電動汽車Zoe 2013年投放

    法國汽車製造商雷諾最近宣佈計畫將Zoe批量投產,2013年投入市場。

    雷諾最近抱怨其銷售緩慢,人們對Twizy毫無熱情,Zoe也許會是雷諾在電動汽車市場上最後的嘗試。雷諾近日宣佈將Zoe批量投產,於2013年開售。Zoe電動馬達輸出功率可達88馬力(65千瓦),扭矩220牛米(163英尺磅),最大速度可達84 英里/小時。此款車在英國定價為14444英鎊(約合人民幣144296元)。

    Zoe是雷諾最新推出的電動汽車,車上將裝配R-Link多媒體平板電腦、電動氣候控制系統和電池充電器等裝置。雷諾R-Link多媒體平板電腦將配置7英寸的觸屏顯示幕,具有語音辨識系統,可以控制一些主要功能,如,音訊系統、電動汽車駕駛指導系統和Carminat TomTom即時導航系統。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※超省錢租車方案

    ※教你寫出一流的銷售文案?

    網頁設計最專業,超強功能平台可客製化

  • 聆風全球銷量42700輛 日產高管稱令人感到“失望”

    日產公司近日發佈了2012財年第二季度報告,日產首席運營官賀俊之稱,目前,日產聆風的全球銷量為42700輛。他同時表示,整體銷量不佳令人感到“失望”。

    截至目前為止,日產聆風在日本的銷量為19000輛,約占全球銷量的一半。日產公司此前制定了2012年在美國銷售2萬輛聆風的目標,雖然現狀與之相距甚遠,但賀俊之堅稱,日產將繼續電動汽車的開發,拓展電動汽車市場。

    賀俊之指出,日產聆風上市兩周年將至,兩年的經驗讓日產公司瞭解了阻礙消費者購買電動汽車的原因、消費者的使用和充電習慣等,這些資訊有利於未來日產團隊採取措施應對相關問題。

    值得一提的是,今年10月份,日產聆風共售出1579輛,較9月份的984輛大幅增加,並遠高於8月份685輛的銷量。正如賀俊之所說,作為第一批大規模生產電動汽車的企業,日產將致力於“零排放”工作,希望民眾能理解日產肩負的重任。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※教你寫出一流的銷售文案?

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 中國汽車技術研究中心正在制定新能源車碰撞安全標準

    中國汽車技術研究中心副主任高和生日前指出,最大的隱患在於電池,在受到撞擊之後電池會變形、起火甚至爆炸,這對汽車的安全帶來了巨大的隱患,因此中心目前也正在考慮制定新能源汽車的相關安全標準。但並沒有透露具體的出臺時間。

    隨著國家的大力宣導,目前新能源汽車的發展正在逐步提速,雖然私人購車數量不多,但越來越多的新能源汽車已經逐步進入到如計程車等服務領域,而近一段時間頻頻出現的安全問題,也讓人們對新能源汽車產生了擔憂。

    同時高和生透露,C-NCAP未來將把進口車碰撞作為工作重點之一,儘管廠家會很反對,但是本著對消費者負責的態度,我們還是會做這個事情,我們將從當前熱銷的進口車中選擇車型進行碰撞試驗,而銷量很小的車型則不在考慮範圍。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

    FB行銷專家,教你從零開始的技巧