天天看點

項目之關于Summernote的圖檔處理和基于SpringMVC的檔案上傳(11)

最後,關于顯示分頁,首先要使得以前加載資料的函數是支援頁碼參數的:

loadMyQuestions: function (page) {
    if (!page || page < 1) {
        page = 1;
    }
    $.ajax({
        url: '/api/v1/questions/my',
        data: 'page=' + page,
        // 省略後續代碼
    });
}      

關于分頁的頁面部分的代碼:

<nav aria-label="Page navigation example">
    <div class="pagination">
        <a class="page-item page-link" href="#"
           v-on:click.prevent="loadMyQuestions(pageInfo.prePage)">上一頁</a>

        <a class="page-item page-link " href="#"
           v-for="i in pageInfo.navigatepageNums"
           v-on:click.prevent="loadMyQuestions(i)"
           v-bind:class="{ 'bg-primary text-light': i == pageInfo.pageNum }">
            <span v-text="i">1</span>
        </a>

        <a class="page-item page-link" href="#"
           v-on:click.prevent="loadMyQuestions(pageInfo.nextPage)">下一頁</a>
    </div>
</nav>

      

在my.js中,在屬性中聲明pageInfo:

data: {

   questions: [],

   pageInfo: null

}

當擷取資料後,添加:

questionsApp.pageInfo = data;

至此,頁面的顯示已完成,關于my.js的完整代碼:

let questionsApp = new Vue({
    el: '#questionsApp',
    data: {
        questions: [
            {
                statusText: '已解決',
                statusClass: 'badge-success',
                title: '這是第1個問題',
                content: '很<b>嚴肅</b>的提出了第1個問題',
                tags: [
                    { id: 8, name: 'Java基礎' },
                    { id: 12, name: 'Spring' },
                    { id: 15, name: 'SpringBoot' }
                ],
                userNickName: '天下第一',
                hits: '826',
                createdTimeText: '8小時之前',
                tagImage: '/img/tags/8.jpg'
            },
            {
                statusText: '未回複',
                statusClass: 'badge-warning',
                title: '這是第2個問題',
                content: '我也不告訴你是什麼問題……',
                tags: [
                    { id: 10, name: 'MySQL' },
                    { id: 17, name: 'SpringSecurity' }
                ],
                userNickName: '天下第一',
                hits: '537',
                createdTimeText: '15小時之前',
                tagImage: '/img/tags/10.jpg'
            }
        ],
        pageInfo: null
    },
    methods: {
        loadMyQuestions: function (page) {
            if (!page || page < 1) {
                page = 1;
            }
            $.ajax({
                url: '/api/v1/questions/my',
                data: 'page=' + page,
                success: function (json) {
                    // json.data.list
                    let data = json.data;
                    let questions = data.list;
                    let statusTexts = ['未回複', '未解決', '已解決'];
                    let statusClasses = ['badge-warning', 'badge-info', 'badge-success'];
                    for (let i = 0; i < questions.length; i++) {
                        questions[i].statusText = statusTexts[questions[i].status];
                        questions[i].statusClass = statusClasses[questions[i].status];
                        questions[i].tagImage = '/img/tags/' + questions[i].tags[0].id + '.jpg';

                        let now = new Date().getTime();
                        let pastTime = (now - new Date(questions[i].createdTime).getTime()) / 1000;
                        let createdTimeText = "未知時間";
                        if (pastTime < 60) { // 不足1分鐘
                            createdTimeText = "剛剛";
                        } else if (pastTime < 60 * 60) { // 不足1小時
                            createdTimeText = parseInt(pastTime / 60) + "分鐘前";
                        } else if (pastTime < 60 * 60 * 24) {
                            createdTimeText = parseInt(pastTime / 60 / 60) + "小時前";
                        } else {
                            createdTimeText = parseInt(pastTime / 60 / 60 / 24) + "天前";
                        }

                        questions[i].createdTimeText = createdTimeText;
                    }
                    questionsApp.questions = questions;
                    questionsApp.pageInfo = data;
                }
            });
        }
    },
    created: function () {
        this.loadMyQuestions();
    }
});

      

46. 關于Summernote的圖檔處理

使用Summernote富文本編輯器時,當需要處理圖檔時,會自動将圖檔轉換為Base64編碼,當送出問題時,圖檔的Base64編碼會作為“問題正文”的一部分送出到伺服器端,最終,會被存儲到資料庫中!使用這種做法,會急劇增加資料庫所占用的存儲空間,對資料庫的檢索性能也會産生影響,不利于資料庫的管理和維護,同時,由于圖檔已經轉換為Base64編碼作為正文的一部分資料,也不利于管理圖檔!

Summernote允許在配置Summernote富文本編輯器時自定義回調函數,該函數會在使用者填寫正文時選擇圖檔會自動調用,則開發人員可以配置這個回調函數,當使用者選擇圖檔後,将圖檔以檔案的形式直接上傳到伺服器端,當上傳成功後,再将圖檔的路徑傳回到用戶端,插入到Summernote中即可!

最後,在Summernote組織的“問題正文”中,關于圖檔可能就隻是一段例如<img src="http://localhost:8080/1.jpg />這樣的代碼,就能夠減少資料庫的存儲資料量,同時,對于檔案的管理也會非常直覺。

47. 基于SpringMVC的檔案上傳

【本知識點的案例為:fileupload】

關于檔案上傳,在HTTP協定中規定:

必須使用POST方式送出請求;

在HTML表單中必須配置enctype="multipart/form-data";

另外,在HTML表單中必須使用<input type="file" />控件。

是以,可以将頁面設計為:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上傳圖檔</title>
</head>
<body>
<h1>上傳圖檔</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
    <p>請選擇您要上傳的檔案:<input type="file" name="image"></p>
    <p><input type="submit" value="上傳"></p>
</form>
</body>
</html>

      

SpringMVC架構處理檔案上傳是基于commons-fileupload的,如果使用SpringMVC架構,需要自行添加這個依賴,如果使用SpringBoot架構則不需要,已經内置添加了。

在SpringMVC架構中,在控制器端會使用MultipartFile接口類型的參數來接收用戶端送出的上傳資料,在處理請求的方法中,直接聲明這個接口類型的參數即可,參數名應該與用戶端送出請求時的名稱保持一緻!在處理請求的過程中,調用MutlipartFile接口對象的void transferTo(File dest)方法就可以将圖檔保持到參數dest對應的檔案位置。

可以在伺服器端建立控制器處理上傳請求:

@RestController
public class FileUploadController {
    @RequestMapping("/upload")
    public String upload(MultipartFile image) throws IOException {
        image.transferTo(new File("d:/1.jpg"));
        return "OK";
    }
}      

注意:SpringBoot預設限制了上傳檔案的大小為1M / 10M(根據SpringBoot版本不同存在差異)。

關于檔案名的處理:

檔案名必須保證唯一,不要出現“覆寫上傳”的現象(即使你認為原有的檔案沒有用了,也不要覆寫);

擴充名應該與原始擴充名(檔案在用戶端裝置中的名稱)保持一緻,注意:如果某個檔案全名中隻有第1位是小數點,并沒有更多的小數點,是表示該檔案在Linux / MacOS中是隐藏檔案,小數點右側的并不是擴充名!

示例代碼:

@RequestMapping("/upload")
public String upload(MultipartFile image) throws IOException {
    String parent = "d:/";

    // 處理檔案名
    // 關于檔案名的政策:時間 + 随機數
    // 無論目前上傳功能是用于哪個用途,檔案名必須唯一
    String filename = UUID.randomUUID().toString();

    // 處理擴充名
    // 擷取原始檔案名
    String originalFilename = image.getOriginalFilename();
    System.out.println("originalFilename=" + originalFilename);
    // 暫定擴充名空字元串
    String suffix = "";
    // 如果原始檔案名中存在有效的擴充名,則截取
    int beginIndex = originalFilename.lastIndexOf(".");
    if (beginIndex > 0) {
        suffix = originalFilename.substring(beginIndex);
    }

    // 得到完整的檔案名
    String child = filename + suffix;

    // 儲存上傳的檔案
    image.transferTo(new File(parent, child));
    return "OK";
}

      

後續,使用以上parent作為上傳的檔案夾即可。

在處理上傳時,關于MultipartFile的常用API有:

boolean isEmpty():判斷上傳的檔案是否為空,如果在表單中沒有選擇檔案,或選擇的檔案是0位元組的,即為空;

long getSize():擷取檔案大小,以位元組為機關;

String contentType:擷取文檔的MIME類型;

String getOriginalFilename():擷取上傳的檔案的原始檔案名;

void transferTo(File dest):儲存上傳的檔案。