項(xiàng)目關(guān)于Summernote的圖片處理和基于SpringMVC的文件上傳(10)

      網(wǎng)友投稿 872 2022-05-30

      45. 我的問答列表-前端頁面

      在index.html頁面,先找到整個(gè)列表區(qū)域的父級(jí),為其添加id,以便于創(chuàng)建Vue對(duì)象:

      1

      2

      找到每個(gè)問題的顯示區(qū)域,為這個(gè)區(qū)域的根級(jí)添加v-for以循環(huán)顯示:

      1

      2

      關(guān)于問題的狀態(tài):

      已解決

      1

      問題的標(biāo)題:

      eclipse 如何導(dǎo)入項(xiàng)目?

      1

      問題的內(nèi)容:

      eclipse 如何導(dǎo)入項(xiàng)目?

      1

      2

      3

      4

      5

      問題的標(biāo)簽列表:

      Java基礎(chǔ)

      1

      2

      3

      右下角的更多信息:

      風(fēng)繼續(xù)吹 12瀏覽 13分鐘前

      1

      2

      3

      顯示圖片:

      1

      完成后,先利用以上模擬的數(shù)據(jù)進(jìn)行測(cè)試,也就是直接打開瀏覽器,觀察運(yùn)行效果與預(yù)期是否相符!

      測(cè)試完成后,在my.js中,向服務(wù)器端發(fā)送請(qǐng)求獲取真實(shí)的數(shù)據(jù),并用于顯示頁面:

      let questionsApp = new Vue({ el: '#questionsApp', data: { questions: [] }, methods: { loadMyQuestions: function () { $.ajax({ url: '/api/v1/questions/my', success: function (json) { // json.data.list let data = json.data; let questions = data.list; let statusTexts = ['未回復(fù)', '未解決', '已解決']; 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'; questions[i].createdTimeText = "未知時(shí)間"; } questionsApp.questions = questions; } }); } }, created: function () { this.loadMyQuestions(); } });

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      關(guān)于將時(shí)間顯示為“剛剛” / “xx分鐘前”等格式的代碼:

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      最后,關(guān)于顯示分頁,首先要使得以前加載數(shù)據(jù)的函數(shù)是支持頁碼參數(shù)的:

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      關(guān)于分頁的頁面部分的代碼:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

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

      data: { questions: [], pageInfo: null }

      1

      2

      3

      4

      當(dāng)獲取數(shù)據(jù)后,添加:

      questionsApp.pageInfo = data;

      1

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

      let questionsApp = new Vue({ el: '#questionsApp', data: { questions: [ { statusText: '已解決', statusClass: 'badge-success', title: '這是第1個(gè)問題', content: '很嚴(yán)肅的提出了第1個(gè)問題', tags: [ { id: 8, name: 'Java基礎(chǔ)' }, { id: 12, name: 'Spring' }, { id: 15, name: 'SpringBoot' } ], userNickName: '天下第一', hits: '826', createdTimeText: '8小時(shí)之前', tagImage: '/img/tags/8.jpg' }, { statusText: '未回復(fù)', statusClass: 'badge-warning', title: '這是第2個(gè)問題', content: '我也不告訴你是什么問題……', tags: [ { id: 10, name: 'MySQL' }, { id: 17, name: 'SpringSecurity' } ], userNickName: '天下第一', hits: '537', createdTimeText: '15小時(shí)之前', 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 = ['未回復(fù)', '未解決', '已解決']; 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 = "未知時(shí)間"; if (pastTime < 60) { // 不足1分鐘 createdTimeText = "剛剛"; } else if (pastTime < 60 * 60) { // 不足1小時(shí) createdTimeText = parseInt(pastTime / 60) + "分鐘前"; } else if (pastTime < 60 * 60 * 24) { createdTimeText = parseInt(pastTime / 60 / 60) + "小時(shí)前"; } else { createdTimeText = parseInt(pastTime / 60 / 60 / 24) + "天前"; } questions[i].createdTimeText = createdTimeText; } questionsApp.questions = questions; questionsApp.pageInfo = data; } }); } }, created: function () { this.loadMyQuestions(); } });

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      46. 關(guān)于Summernote的圖片處理

      使用Summernote富文本編輯器時(shí),當(dāng)需要處理圖片時(shí),會(huì)自動(dòng)將圖片轉(zhuǎn)換為Base64編碼,當(dāng)提交問題時(shí),圖片的Base64編碼會(huì)作為“問題正文”的一部分提交到服務(wù)器端,最終,會(huì)被存儲(chǔ)到數(shù)據(jù)庫中!使用這種做法,會(huì)急劇增加數(shù)據(jù)庫所占用的存儲(chǔ)空間,對(duì)數(shù)據(jù)庫的檢索性能也會(huì)產(chǎn)生影響,不利于數(shù)據(jù)庫的管理和維護(hù),同時(shí),由于圖片已經(jīng)轉(zhuǎn)換為Base64編碼作為正文的一部分?jǐn)?shù)據(jù),也不利于管理圖片!

      Summernote允許在配置Summernote富文本編輯器時(shí)自定義回調(diào)函數(shù),該函數(shù)會(huì)在用戶填寫正文時(shí)選擇圖片會(huì)自動(dòng)調(diào)用,則開發(fā)人員可以配置這個(gè)回調(diào)函數(shù),當(dāng)用戶選擇圖片后,將圖片以文件的形式直接上傳到服務(wù)器端,當(dāng)上傳成功后,再將圖片的路徑返回到客戶端,插入到Summernote中即可!

      最后,在Summernote組織的“問題正文”中,關(guān)于圖片可能就只是一段例如 上傳圖片

      上傳圖片

      請(qǐng)選擇您要上傳的文件:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      SpringMVC框架處理文件上傳是基于commons-fileupload的,如果使用SpringMVC框架,需要自行添加這個(gè)依賴,如果使用SpringBoot框架則不需要,已經(jīng)內(nèi)置添加了。

      在SpringMVC框架中,在控制器端會(huì)使用MultipartFile接口類型的參數(shù)來接收客戶端提交的上傳數(shù)據(jù),在處理請(qǐng)求的方法中,直接聲明這個(gè)接口類型的參數(shù)即可,參數(shù)名應(yīng)該與客戶端提交請(qǐng)求時(shí)的名稱保持一致!在處理請(qǐng)求的過程中,調(diào)用MutlipartFile接口對(duì)象的void transferTo(File dest)方法就可以將圖片保持到參數(shù)dest對(duì)應(yīng)的文件位置。

      可以在服務(wù)器端創(chuàng)建控制器處理上傳請(qǐng)求:

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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      注意:SpringBoot默認(rèn)限制了上傳文件的大小為1M / 10M(根據(jù)SpringBoot版本不同存在差異)。

      關(guān)于文件名的處理:

      文件名必須保證唯一,不要出現(xiàn)“覆蓋上傳”的現(xiàn)象(即使你認(rèn)為原有的文件沒有用了,也不要覆蓋);

      擴(kuò)展名應(yīng)該與原始擴(kuò)展名(文件在客戶端設(shè)備中的名稱)保持一致,注意:如果某個(gè)文件全名中只有第1位是小數(shù)點(diǎn),并沒有更多的小數(shù)點(diǎn),是表示該文件在Linux / MacOS中是隱藏文件,小數(shù)點(diǎn)右側(cè)的并不是擴(kuò)展名!

      示例代碼:

      @RequestMapping("/upload") public String upload(MultipartFile image) throws IOException { String parent = "d:/"; // 處理文件名 // 關(guān)于文件名的策略:時(shí)間 + 隨機(jī)數(shù) // 無論當(dāng)前上傳功能是用于哪個(gè)用途,文件名必須唯一 String filename = UUID.randomUUID().toString(); // 處理擴(kuò)展名 // 獲取原始文件名 String originalFilename = image.getOriginalFilename(); System.out.println("originalFilename=" + originalFilename); // 暫定擴(kuò)展名空字符串 String suffix = ""; // 如果原始文件名中存在有效的擴(kuò)展名,則截取 int beginIndex = originalFilename.lastIndexOf("."); if (beginIndex > 0) { suffix = originalFilename.substring(beginIndex); } // 得到完整的文件名 String child = filename + suffix; // 保存上傳的文件 image.transferTo(new File(parent, child)); return "OK"; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      關(guān)于保存文件的路徑,首先,所有的上傳都是為了下載的,所以,必須保證上傳的文件夾是可以被訪問到的文件夾,例如將文件上傳到Tomcat的部署文件夾中,對(duì)于使用SpringBoot開發(fā)項(xiàng)目來說,也可以理解為“需要將文件上傳到static文件夾或webapp文件夾下”!

      可選的解決方案有:

      String parent = request.getServletContext().getRealPath("20200725"); System.out.println("parent=" + parent); File parentFile = new File(parent); if (!parentFile.exists()) { parentFile.mkdirs(); }

      1

      2

      3

      4

      5

      6

      以上做法是將文件直接上傳到項(xiàng)目的webapp文件夾中,這樣做不便于管理文件,因?yàn)轫?xiàng)目文件和上傳的文件都在同個(gè)文件夾之下!

      SpringMVC / SpringBoot可以自定義“資源目錄”,當(dāng)某個(gè)文件夾被設(shè)置為“資源目錄”時(shí),該目錄下的內(nèi)容是可以直接通過HTTP協(xié)議進(jìn)行訪問的!相當(dāng)于static或webapp文件夾。

      在SpringBoot項(xiàng)目的application.properties文件中進(jìn)行配置:

      spring.resources.static-locations=file:d:/upload

      1

      則d:/upload就變成了“資源目錄”,如果在這個(gè)文件夾中添加文件,是可以直接通過HTTP協(xié)議訪問的!

      然后,在application.properties中添加自定義配置,并將自定義配置值用于配置“資源目錄”,并且,由于自定義了資源目錄,原本static就不再是資源目錄了,需要顯式的指定:

      project.upload-location=d:/upload spring.resources.static-locations[0]=classpath:/static spring.resources.static-locations[1]=file:${project.upload-location}

      1

      2

      3

      4

      在控制器中,可以直接讀取到以上配置:

      @Value("${project.upload-location}") private String parent;

      1

      2

      后續(xù),使用以上parent作為上傳的文件夾即可。

      在處理上傳時(shí),關(guān)于MultipartFile的常用API有:

      boolean isEmpty():判斷上傳的文件是否為空,如果在表單中沒有選擇文件,或選擇的文件是0字節(jié)的,即為空;

      long getSize():獲取文件大小,以字節(jié)為單位;

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

      String getOriginalFilename():獲取上傳的文件的原始文件名;

      void transferTo(File dest):保存上傳的文件。

      圖像處理 數(shù)據(jù)庫

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:測(cè)試效能平臺(tái)最佳實(shí)踐 | 解決用戶痛點(diǎn),比堆疊功能更重要!
      下一篇:谷歌移動(dòng)UI框架Flutter教程之Widget
      相關(guān)文章
      亚洲日本va中文字幕久久| 亚洲日本在线电影| 女bbbbxxxx另类亚洲| 亚洲av午夜精品无码专区| 亚洲图片中文字幕| 精品亚洲成AV人在线观看| 亚洲国产精品高清久久久| 国产精品亚洲а∨无码播放 | 中文字幕第13亚洲另类| 国产成人亚洲精品蜜芽影院| 亚洲成aⅴ人片久青草影院按摩| 国产精品亚洲专区在线观看| 亚洲一本之道高清乱码| 亚洲五月综合缴情婷婷| 久久精品国产亚洲AV蜜臀色欲 | 亚洲成人影院在线观看| 亚洲高清无码综合性爱视频| 亚洲国产婷婷香蕉久久久久久| 亚洲精品一级无码中文字幕| 亚洲综合亚洲综合网成人| 亚洲综合日韩久久成人AV| 国产精品亚洲片在线| 久久久亚洲精品视频| 久久亚洲精品成人无码网站| 亚洲精品美女视频| 亚洲AV综合色区无码二区爱AV| 亚洲一区欧洲一区| 亚洲国产成人久久精品大牛影视| 色五月五月丁香亚洲综合网| 亚洲人成国产精品无码| 久久亚洲国产中v天仙www| 亚洲电影一区二区三区| 亚洲免费观看在线视频| 亚洲欧美中文日韩视频| 亚洲国产精品国产自在在线| 亚洲中文字幕无码中文字在线| 久久精品国产亚洲香蕉| 亚洲日韩国产精品无码av| 亚洲综合一区无码精品| 国产精品亚洲专区在线播放| 亚洲午夜久久久久久噜噜噜|