Vue SpringBoot實現Html和Markdown格式內容(含圖片上傳)儲存到MySQL
實現功能
- 本文程式碼實現了前端Html、Markdown格式的博文儲存到MySQL的功能。
- 包括文章中圖片的上傳,在使用者選擇圖片後就將其傳到後端並將圖片的連結返回給前端,填入到指定的位置。
遇到的問題
- 由於Markdown編輯器原因,返回的圖片路徑不能有\與空格
- 如果遇到第二次進入編輯頁面不能顯示文章內容,那麼在下方getArticle()方法中,處理響應的最後一行加入
// 解決第二次進入不能顯示內容bug this.$refs.md.d_value = response.data.markdownContent
- Html格式內容中的部分特殊符號會被JAVA替換掉,導致回顯的頁面樣式有出入。建議不使用Html格式,使用Markdown格式
前端
前端是用Vue-cli搭建的專案工程,執行在WebStorm中。
安裝依賴
-
資料請求相關
npm install axios --save npm install qs --save
-
安裝Markdown編輯器
npm install mavon-editor --save
使用mavon-editor,請自行參考如何使用。目前不支援流程圖、序列圖、甘特圖
-
其他依賴
npm install style-loader npm install css-loader npm install sass-loader npm install babel-loader --save
在main.js中引入mavonEditor
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
Vue.use(mavonEditor)
ArticleMarkdown.vue元件,實現Markdown博文的存取
程式碼導讀
getArticle():通過文章id獲取內容
saveArticle():提交博文內容到後端。同時提交了html、markdown格式的內容,見
var htmlCode = this.$refs.md.d_render;
var markdownCode = this.$refs.md.d_value;
imgAdd(pos, file):上傳單張圖片。file圖片物件,pos圖片下標,後端返回圖片連結地址時,用於定位
imgDel(pos):刪除圖片
mulUploadimg() :上傳多張圖片。圖片物件存放在data中img_file物件中
imgDelMul(pos):刪除多張圖片。
原始碼
<template>
<div>
<mavon-editor ref="md" class="md" v-model="sqlData.markdown" @imgAdd="imgAdd" @imgDel="imgDel" @save="saveArticle"/>
</div>
</template>
<script>
import axios from 'axios'
import qs from 'qs'
const area_axios = axios.create({
headers: {'Content-Type': 'application/json;charset=utf-8',},// 設定傳輸內容的型別和編碼
withCredentials: true,// 指定某個請求應該傳送憑據
})
const file_axios = axios.create({
headers: {'Content-Type': 'multipart/form-data',},// 設定傳輸內容的型別和編碼
withCredentials: true,// 指定某個請求應該傳送憑據
})
const area_form_axios = axios.create({
headers: {'Content-Type': 'application/x-www-form-urlencoded',},// 設定傳輸內容的型別和編碼
withCredentials: true,// 指定某個請求應該傳送憑據
})
export default {
name: "Markdown",
data() {
return {
sqlData:{
markdown:'',
html:''
},
img_file: {},// 一次上次多張圖片時使用
};
},
mounted:function (){
getArticle()
},
methods: {
// 獲取文章
getArticle(){
area_form_axios.get('/api/get',{
params:{id: 12 }
},)
.then(response => {
console.log(this.sqlData)
this.sqlData = response.data
})
.catch(err => {
alert("請求失敗")
})
},
// 儲存文章
saveArticle(){
var htmlCode = this.$refs.md.d_render;
var markdownCode = this.$refs.md.d_value;
if(htmlCode.length == 0 || markdownCode.length == 0){
alert("請填寫")
return;
}
area_axios({
url: '/api/add',
method: 'post',
data: JSON.stringify({'markdown':markdownCode,'html':htmlCode}),
}).then((response) => {
if(response.data > 0){
alert("成功")
}else {
alert("失敗")
}
})
},
// 新增圖片
imgAdd(pos, file){
console.log("pos:"+pos)
// 第一步.將圖片上傳到伺服器.
var formdata = new FormData();
formdata.append('pic', file);
file_axios({
url: '/api/img_upload',
method: 'post',
data: formdata,
}).then((response) => {
// 第二步.將返回的url替換到文字原位置
var url = response.data;
//通過引入物件獲取: import {mavonEditor} from ... 等方式引入後,此時$vm即為mavonEditor
//通過$refs獲取: html宣告ref : <mavon-editor ref=md ></mavon-editor>, 此時$vm為 this.$refs.md`
this.$refs.md.$img2Url(pos, url);
})
},
// 刪除圖片
imgDel(pos){
console.log("imgDel pos:"+pos)
},
// 多張圖片
mulUploadimg(){
// 第一步.將圖片上傳到伺服器.
var formdata = new FormData();
for(var _img in this.img_file){
debugger
// 後臺需要圖片的key一致
formdata.append('pics', this.img_file[_img]);
}
file_axios({
url: '/api/mul_img_upload',
method: 'post',
data: formdata,
}).then((res) => {
/**
* 例如:返回資料為 res = [[pos, url], [pos, url]...]
* pos 為原圖片標誌(0)
* url 為上傳後圖片的url地址
*/
// 第二步.將返回的url替換到文字原位置![...](0) -> ![...](url)
var idx_url = res.data;
idx_url.forEach(item => {
//通過引入物件獲取: import {mavonEditor} from ... 等方式引入後,此時$vm即為mavonEditor
//通過$refs獲取: html宣告ref : <mavon-editor ref=md ></mavon-editor>, 此時$vm為 this.$refs.md`
this.$refs.md.$img2Url(item[0], item[1]);
});
})
},
// 多張圖片
imgDelMul(pos){
console.log("imgDel pos:"+pos)
delete this.img_file[pos];
},
}
}
</script>
跨域配置
後端
檔案上傳相關配置
application.properties檔案中
spring.servlet.multipart.enabled=true
# 最大支援檔案大小
spring.servlet.multipart.max-file-size=10MB
# 最大支援請求大小
spring.servlet.multipart.max-request-size=50MB
配置攔截器
@Component
public class CrossDomainInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 允許客戶端攜帶跨域cookie,此時origin值不能為“*”,只能為指定單一域名。!!開發時不要使用localhost訪問
response.setHeader("Access-Control-Allow-Credentials", "true");
// 允許指定域訪問跨域資源
//response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:9006, http://127.0.0.1:8080");
response.setHeader("Access-Control-Allow-Origin", origin);// *
// 允許瀏覽器傳送的請求訊息頭
//response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
// 允許瀏覽器在預檢請求成功之後傳送的實際請求方法名
//response.setHeader("Access-Control-Allow-Methods", "DEFAULT,POST,PATCH,PUT,OPTIONS,DELETE,HEAD");
response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
// 瀏覽器快取預檢請求結果時間,單位:秒
response.setHeader("Access-Control-Max-Age", "86400");
return true;
}
}
資料介面
@Controller
@RequestMapping("")
public class MarkdownController {
/**
* 獲取文章
* id: 文章id
* @author YSL
* 2019-03-04 15:38
*/
@GetMapping("/get")
@ResponseBody
public Bean test(@RequestParam("id")Integer id){
// 獲取資料庫中的資料,請自行實現。
return vueMarkdownMapper.query(id);
}
/**
* 儲存文章到資料庫。
* bean:前端傳回JSON.stringify({'markdown':markdownCode,'html':htmlCode})格式的資料即可
* @author YSL
* 2019-03-04 15:26
*/
@PostMapping("/add")
@ResponseBody
public int test(@RequestBody Bean bean){
return vueMarkdownMapper.add(bean);// 儲存資料到資料庫,請自行實現
}
}
圖片上傳
程式碼導讀
@RequestMapping("/img_upload"):單張圖片上傳,上傳到blog_files/pictures目錄下,返回圖片url。
@RequestMapping("/mul_img_upload"):多張圖片上傳。上傳到blog_files/pictures目錄下,返回new String[]{圖片下標, 圖片url}格式的list。
fileUpload():檔案上傳。上傳到blog_files/files目錄下。
upload():真正實現檔案上傳的方法,基於MultipartFile實現
說明
- 圖片與檔案都是上傳到tomcat/webapps/blog_files/目錄下,blog_files是我專門用來儲存圖片的一個web工程,方便通過http訪問到圖片,給前端返回的圖片地址也是http格式的。
- 在圖片和檔案上傳的同時會備份,案例總備份路徑:D:/webserver_bak/blog/
原始碼
public class FileController {
/**
* 圖片上傳(一張)
* @param pic 需要上傳的圖片
* @return 圖片url
* @author YSL
* 2019-03-01 17:14
*/
@RequestMapping("/img_upload")
@ResponseBody
public String imgUpload(@RequestParam(value = "pic", required = false) MultipartFile pic, HttpServletRequest request){
List<String> urlList = upload(new MultipartFile[]{pic}, "pictures", request);
return urlList != null ? urlList.get(0) : "";
}
/**
* 圖片上傳(多張)
* @param pics 需要上傳的圖片
* @return 圖片下標和url
* @author YSL
* 2019-03-01 17:14
*/
@RequestMapping("/mul_img_upload")
@ResponseBody
public List<String[]> imgUpload(@RequestParam(value = "pics", required = false) MultipartFile[] pics, HttpServletRequest request){
List<String> urlList = upload(pics, "pictures", request);
List<String[]> list = new ArrayList<>();
for (int i = 0; i < urlList.size() ; i++) {
String[] idx_url = new String[2];
// 圖片下標
idx_url[0]=i+"";
// 拼接url
idx_url[1] = urlList.get(i);
list.add(idx_url);
}
return list;
}
/**
* 檔案上傳
* @param files 需要上傳的檔案
* @return 檔案url
* @author YSL
* 2019-03-01 17:14
*/
@RequestMapping("/file_upload")
@ResponseBody
public List<String> fileUpload(@RequestParam(value = "files", required = false) MultipartFile[] files, HttpServletRequest request){
List<String> urlList = upload(files, "pictures", request);
return urlList;
}
/**
* 檔案/圖片上傳。並做備份<br/>
* 路徑不能有反斜線和空格 <br/>
* 上傳路徑:.../webapps/blog_files/pictures/20190301/圖片 <br/>
* 上傳路徑:.../webapps/blog_files/files/20190301/檔案 <br/>
* 備份路徑:.../webserver_bak/blog/pictures/20190301/圖片 <br/>
* 備份路徑:.../webserver_bak/blog/files/20190301/檔案
* @param files 需要上傳的檔案
* @param categoryPath 類別路徑,pictures/files
* @return 上傳成功,返回檔案url
* @author YSL
* 2019-03-01 16:45
*/
public List<String> upload(MultipartFile[] files, String categoryPath, HttpServletRequest request){
// 非空判定
if(files == null || files.length == 0){
return new ArrayList<>();
}
// 專門存放檔案工程名稱(是一個javaweb工程,方便圖片直接通過http訪問)
String fileProject = "blog_files";
// 備份路徑
String bakPath = "D:/webserver_bak/blog/";
//http://localhost:7989/
String ipPort = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort() + "/";
/**
* 獲取專案絕對路徑,格式,D:\tomcats\apache-tomcat-8.0.52\webapps\boot\。
* markdown編輯器圖片路徑不能有\,所以替換為/
* 注意:.replace("//", "/"); 與 replaceAll("\\\\", "/");
*/
String rootPath = request.getSession().getServletContext().getRealPath("").replaceAll("\\\\", "/");
// 專案路徑。/boot
String contextPath = request.getContextPath();
rootPath = rootPath.substring(0, rootPath.lastIndexOf(contextPath.replace("/","")));
StringBuilder fileRoot = new StringBuilder("");
// 工程名稱
fileRoot.append(fileProject);
fileRoot.append("/");
// 類別目錄
fileRoot.append(categoryPath);
fileRoot.append("/");
// 檔案目錄,圖片上傳失敗時使用
String picRootPath = fileRoot.toString();
String day = new SimpleDateFormat("yyyyMMdd").format(new Date());
// 日期目錄
fileRoot.append(day);
fileRoot.append("/");
// 檔案最終儲存目錄
String fileDir = fileRoot.toString();
List<String> list = new ArrayList<>();
for (MultipartFile multipartFile : files) {
// 檔名稱。markdown編輯器圖片路徑不能有空格
String upFileName = multipartFile.getOriginalFilename().replaceAll("\\s+", "");
String filename = new SimpleDateFormat("HHmmss").format(new Date()) + "_" + UUID.randomUUID().toString() + "_" + upFileName;
String filePathName = rootPath + fileDir + filename;
File destFile = new File(filePathName);
try {
// 複製臨時檔案到指定目錄下, 會建立沒有的目錄
FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), destFile);
// 拼接url
list.add(ipPort + fileDir + filename);
// 備份
File bakFile = new File(bakPath + fileDir + filename);
FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), bakFile);
} catch (UnsupportedEncodingException e2) {
e2.printStackTrace();
if("pictures".equals(categoryPath)){
// 預設圖片
list.add(picRootPath+"default.jpg");
}else{
list.add("");
}
} catch (IOException e) {
e.printStackTrace();
if("pictures".equals(categoryPath)){
// 預設圖片
list.add(picRootPath+"default.jpg");
}else{
list.add("");
}
}
}
return list;
}
}
資料庫
欄位名 | 型別 | 長度 | 備註 |
---|---|---|---|
id | int | 預設 | 文章id |
markdown | text | markdown格式內容 | |
html | text | html格式內容 |
參考
https://blog.csdn.net/qq_32407233/article/details/84656914
https://blog.csdn.net/wangjun5159/article/details/48809427
https://segmentfault.com/q/1010000016563395
相關文章
- vue 實現貼上上傳圖片Vue
- 本地Markdown上傳圖片
- 原生JS實現base64圖片下載-圖片儲存到本地JS
- element-ui+Vue實現的圖片上傳UIVue
- 使用Vue實現圖片上傳的三種方式Vue
- 用Vue來實現圖片上傳多種方式Vue
- 安卓上傳圖片到伺服器並儲存到電腦本地安卓伺服器
- 在ThinkPHP5框架下引入Ueditor並實現向七牛雲物件儲存上傳圖片同時將圖片資訊儲存到MySQL資料庫,同時實現lazyload懶載入PHP框架物件MySql資料庫
- django 實現圖片上傳和顯示操作Django
- springboot+wangEditor圖片上傳Spring Boot
- Vue +Element Ui 使用Upload元件實現多圖片上傳VueUI元件
- formData原生實現圖片上傳ORM
- vue+springboot實現圖片的上傳及回顯失敗問題的解決VueSpring Boot
- 諮詢數學公式儲存到mysql中 非圖片形式儲存公式MySql
- 使用 HTML5 Canvas 實現圓形圖片裁剪並上傳HTMLCanvas
- springboot整合百度富文字編輯器ueditor實現圖片上傳和檔案上傳功能Spring Boot
- Element-UI框架 —— Upload 上傳(圖片上傳格式和大小判斷)UI框架
- canvas元件繪製的內容匯出生成圖片儲存到相簿後開啟異常Canvas元件
- vue圖片預覽上傳Vue
- Vue圖片裁剪上傳元件Vue元件
- Nodejs如何把接收圖片base64格式儲存為檔案儲存到伺服器上NodeJS伺服器
- UEditor實現單張圖片上傳至騰訊雲(物件儲存服務)功能(html5物件HTML
- vue 上傳圖片進行壓縮圖片Vue
- PHP實現圖片(檔案)上傳PHP
- java,springboot + thymeleaf 上傳圖片、刪除圖片到伺服器、本地,壓縮圖片上傳(有些圖片會失真),原圖上傳JavaSpring Boot伺服器
- Vue專案中最簡單的使用整合UEditor方式,含圖片上傳Vue
- node+express實現圖片上傳功能Express
- layui中實現上傳圖片壓縮UI
- 通過API介面實現圖片上傳API
- 爬取微博圖片資料存到Mysql中遇到的各種坑mysql儲存圖片爬取微博圖片MySql
- 短視訊平臺開發,圖片上傳和圖片預覽功能實現
- Alfred Workflow 一鍵上傳圖片到 GitHub 返回 MarkdownAlfredGithub
- springboot專案上傳儲存圖片到七牛雲伺服器Spring Boot伺服器
- Java實現圖片上傳到伺服器,並把上傳的圖片讀取出來Java伺服器
- 使用freemarker將echarts圖片儲存到word中Echarts
- SpringMVC實現ajax上傳圖片實時預覽SpringMVC
- Qt實現圖片拖拽上傳過濾資料夾內圖片自動搜尋列表展示QT
- vue 圖片上傳到阿里雲ossVue阿里