Skip to content

Commit

Permalink
supported Picture in posts
Browse files Browse the repository at this point in the history
  • Loading branch information
1517005260 committed Jun 8, 2024
1 parent 2dd0643 commit 0510b51
Show file tree
Hide file tree
Showing 10 changed files with 594 additions and 96 deletions.
383 changes: 383 additions & 0 deletions help/8-WindUp/3.5-PictureInPosts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,383 @@
# 帖子发图功能

之前的懒不能偷。。现在应测试需求补上发帖时的图片功能

修改 publish 和 update 界面的逻辑,增加存储图片的逻辑

[参考](https://itmtx.cn/article/199?columnId=12)



1. SecurityConfig新增跨域解决:

```java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// 禁用X-Frame-Options
http.headers(headers -> headers
.frameOptions(frameOptions -> frameOptions.disable())
);
}
```

2. CommunityUtil新返回json接口:

```java
// editor.md 要求返回的 JSON 字符串格式
public static String getEditorMdJSONString(int success, String message, String url) {
JSONObject json = new JSONObject();
json.put("success", success);
json.put("message", message);
json.put("url", url);
return json.toJSONString();
}
```

3. DiscussPostController新增:

在这之前,先配置路径:

develop:

```
community.path.editormdUploadPath=c:/Users/15170/Desktop/community/data
```

produce:

```
community.path.editormdUploadPath=/tmp/uploads/mdPic
```

```java
// 处理帖子上传图片
@RequestMapping(path = "/uploadMdPic", method = RequestMethod.POST)
@ResponseBody
public String uploadMdPic(@RequestParam(value = "editormd-image-file", required = false) MultipartFile file) {

String url = null; // 图片访问地址
try {
// 获取上传文件的名称
String trueFileName = file.getOriginalFilename();
String suffix = trueFileName.substring(trueFileName.lastIndexOf("."));
String fileName = CommunityUtil.genUUID() + suffix;

// 图片存储路径
File dest = new File(editormdUploadPath + "/" + fileName);
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}

// 保存图片到存储路径
file.transferTo(dest);

// 图片访问地址
url = domain + contextPath + "/upload/" + fileName;
System.out.println(url);
} catch (Exception e) {
e.printStackTrace();
return CommunityUtil.getEditorMdJSONString(0, "上传失败", url);
}
return CommunityUtil.getEditorMdJSONString(1, "上传成功", url);
}

// 帖子读取图片
@RequestMapping(path = "/upload/{fileName}", method = RequestMethod.GET)
public void getMdPic(@PathVariable("fileName") String fileName, HttpServletResponse response) {
// 服务器存放路径
String filePath = editormdUploadPath + "/" + fileName;

File file = new File(filePath);
if (!file.exists()) {
System.out.println("文件不存在: " + filePath);
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}

String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);

// 响应文件
response.setContentType("image/" + suffix);
try (
OutputStream os = response.getOutputStream();
FileInputStream fis = new FileInputStream(file);
) {
byte[] buffer = new byte[1024];
int b;
while ((b = fis.read(buffer)) != -1) {
os.write(buffer, 0, b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
```

3. 前端

```html
<script type="text/javascript">
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width: "90%",
height: 640,
syncScrolling: "single",
path: "../editor-md/lib/",
saveHTMLToTextarea: true, // 方便post提交表单
placeholder: "欢迎来到帖子发布界面~ 本论坛支持 Markdown/非Markdown 格式的帖子~",
// 上传图片
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : CONTEXT_PATH + "/discuss/uploadMdPic", // 后端上传图片的服务地址
onload : function() {}
});
</script>
```
注意:这里需要修改官方文件的/plugins/image-dialog/image-dialog.js, 参考[博客](https://blog.csdn.net/ELOVINFG/article/details/103048813)
如下:
```javascript
(function() {
var factory = function (exports) {
var pluginName = "image-dialog";
exports.fn.imageDialog = function() {
var _this = this;
var cm = this.cm;
var lang = this.lang;
var editor = this.editor;
var settings = this.settings;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var imageLang = lang.dialog.image;
var classPrefix = this.classPrefix;
var iframeName = classPrefix + "image-iframe";
var dialogName = classPrefix + pluginName, dialog;
cm.focus();
var loading = function(show) {
var _loading = dialog.find("." + classPrefix + "dialog-mask");
_loading[(show) ? "show" : "hide"]();
};
if (editor.find("." + dialogName).length < 1)
{
var guid = (new Date).getTime();
var action = settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid;
if (settings.crossDomainUpload)
{
action += "&callback=" + settings.uploadCallbackURL + "&dialog_id=editormd-image-dialog-" + guid;
}
var dialogContent = ( (settings.imageUpload) ? "<form action=\"#\" target=\"" + iframeName + "\" method=\"post\" enctype=\"multipart/form-data\" class=\"" + classPrefix + "form\">" : "<div class=\"" + classPrefix + "form\">" ) +
( (settings.imageUpload) ? "<iframe name=\"" + iframeName + "\" id=\"" + iframeName + "\" guid=\"" + guid + "\"></iframe>" : "" ) +
"<label>" + imageLang.url + "</label>" +
"<input type=\"text\" data-url />" + (function(){
return (settings.imageUpload) ? "<div class=\"" + classPrefix + "file-input\">" +
"<input type=\"file\" name=\"" + classPrefix + "image-file\" id=\"" + classPrefix + "image-file\" accept=\"image/*\" />" +
"<input type=\"submit\" value=\"" + imageLang.uploadButton + "\" />" +
"</div>" : "";
})() +
"<br/>" +
"<label>" + imageLang.alt + "</label>" +
"<input type=\"text\" value=\"" + selection + "\" data-alt />" +
"<br/>" +
"<label>" + imageLang.link + "</label>" +
"<input type=\"text\" value=\"http://\" data-link />" +
"<br/>" +
( (settings.imageUpload) ? "</form>" : "</div>");
dialog = this.createDialog({
title : imageLang.title,
width : (settings.imageUpload) ? 465 : 380,
height : 254,
name : dialogName,
content : dialogContent,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
},
buttons : {
enter : [lang.buttons.enter, function() {
var url = this.find("[data-url]").val();
var alt = this.find("[data-alt]").val();
var link = this.find("[data-link]").val();
if (url === "")
{
alert(imageLang.imageURLEmpty);
return false;
}
var altAttr = (alt !== "") ? " \"" + alt + "\"" : "";
if (link === "" || link === "http://")
{
cm.replaceSelection("![" + alt + "](" + url + altAttr + ")");
}
else
{
cm.replaceSelection("[![" + alt + "](" + url + altAttr + ")](" + link + altAttr + ")");
}
if (alt === "") {
cm.setCursor(cursor.line, cursor.ch + 2);
}
this.hide().lockScreen(false).hideMask();
//删除对话框
this.remove();
return false;
}],
cancel : [lang.buttons.cancel, function() {
this.hide().lockScreen(false).hideMask();
//删除对话框
this.remove();
return false;
}]
}
});
dialog.attr("id", classPrefix + "image-dialog-" + guid);
if (!settings.imageUpload) {
return ;
}
var fileInput = dialog.find("[name=\"" + classPrefix + "image-file\"]");
fileInput.bind("change", function() {
var fileName = fileInput.val();
var isImage = new RegExp("(\\.(" + settings.imageFormats.join("|") + "))$", "i"); // /(\.(webp|jpg|jpeg|gif|bmp|png))$/
if (fileName === "")
{
alert(imageLang.uploadFileEmpty);
return false;
}
if (!isImage.test(fileName))
{
alert(imageLang.formatNotAllowed + settings.imageFormats.join(", "));
return false;
}
loading(true);
var submitHandler = function() {
var uploadIframe = document.getElementById(iframeName);
uploadIframe.onload = function() {
loading(false);
var formData = new FormData();
formData.append("editormd-image-file",$("#editormd-image-file")[0].files[0]);
var action = settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid;
let token = $("meta[name= '_csrf']").attr("content");
let header = $("meta[name= '_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options){
xhr.setRequestHeader(header, token);
});
$.ajax({
type:"post",
url:action,
data:formData,
dataType:"json",
async:false,
processData : false, // 使数据不做处理
contentType : false, // 不要设置Content-Type请求头
success:function(data){
// 成功拿到结果放到这个函数 data就是拿到的结果
console.log(data);
if(data.success == 1){
console.log(data.message);
dialog.find("[data-url]").val(data.url);
}else{
alert(data.message);
}
},
});
return false;
};
};
dialog.find("[type=\"submit\"]").bind("click", submitHandler).trigger("click");
});
}
dialog = editor.find("." + dialogName);
dialog.find("[type=\"text\"]").val("");
dialog.find("[type=\"file\"]").val("");
dialog.find("[data-link]").val("http://");
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
var editormd = require("./../../editormd");
factory(editormd);
});
}
}
else
{
factory(window.editormd);
}
})();
```
注意:如果一直显示图片404,应该是虚拟路径映射的问题,springboot为了保护服务器,会隐藏真实路径
只要在WebMvcConfig下增加:
```java
@Value("${community.path.editormdUploadPath}")
private String editormdUploadPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 将上传路径映射到虚拟路径
registry.addResourceHandler("/upload/**").addResourceLocations("file:" + editormdUploadPath + "/");
}
```
Loading

0 comments on commit 0510b51

Please sign in to comment.