当前位置:网站首页>ssm + ftp +ueditor
ssm + ftp +ueditor
2022-06-23 06:50:00 【Small code 2016】
Directory structure

jar package
ueditor.jar
mvn install:install-file -Dfile=ueditor-1.1.2.jar -Dpackaging=jar -DgroupId=com.baidu.ueditor -DartifactId=ueditor -Dversion=1.1.2
<!--
ueditor
mvn install:install-file -Dfile=ueditor-1.1.2.jar -Dpackaging=jar -DgroupId=com.baidu.ueditor -DartifactId=ueditor -Dversion=1.1.2
-->
<dependency>
<groupId>com.baidu.ueditor</groupId>
<artifactId>ueditor</artifactId>
<version>1.1.2</version>
</dependency>
json.jar
Put directly WEB-INF/lib Under the table of contents
spring mvc To configure
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- TODO add to Interface package scanning -->
<!-- Spring Register in container @controller Annotated Bean -->
<context:component-scan base-package="com.laolang.jd.modules.*.web" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- Annotation driven -->
<mvc:annotation-driven />
<!-- Static resource filtering -->
<mvc:default-servlet-handler/>
<mvc:resources mapping="/favicon.ico" location="favicon.ico"/>
<!-- ueditor -->
<mvc:view-controller path="/ueditorconfig" view-name="../../assets/ueditor/jsp/controller"/>
<!-- jsp View -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="104857600" />
<property name="maxInMemorySize" value="4096" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
</beans>
ssm Image upload
Tool class
IDUtils
package com.laolang.jd.common.util;
import java.util.Random;
/**
* Various id Generation strategy
* <p>Title: IDUtils</p>
* <p>Description: </p>
* <p>Company: www.itcast.com</p>
*
* @version 1.0
* @author Into the clouds
* @date 2015 year 7 month 22 On the afternoon of Sunday 2:32:10
*/
public class IDUtils {
/**
* Image name generation
*/
public static String genImageName() {
// Take the long integer value of the current time, including milliseconds
long millis = System.currentTimeMillis();
//long millis = System.nanoTime();
// Add three random numbers
Random random = new Random();
int end3 = random.nextInt(999);
// If there are less than three, make up for it 0
String str = millis + String.format("%03d", end3);
return str;
}
/**
* goods id Generate
*/
public static long genItemId() {
// Take the long integer value of the current time, including milliseconds
long millis = System.currentTimeMillis();
//long millis = System.nanoTime();
// Add two random numbers
Random random = new Random();
int end2 = random.nextInt(99);
// If there are less than two people, fill in front of 0
String str = millis + String.format("%02d", end2);
long id = new Long(str);
return id;
}
}
FtpUtil
package com.laolang.jd.common.util;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import java.io.*;
/**
* ftp Upload and download tool class
* <p>Title: FtpUtil</p>
* <p>Description: </p>
* <p>Company: www.itcast.com</p>
*
* @version 1.0
* @author Into the clouds
* @date 2015 year 7 month 29 On the afternoon of Sunday 8:11:51
*/
public class FtpUtil {
/**
* Description: towards FTP Server upload file
*
* @param host FTP The server hostname
* @param port FTP Server port
* @param username FTP Login account
* @param password FTP The login password
* @param basePath FTP Server base directory
* @param filePath FTP Server file storage path . For example, store by date :/2015/01/01. The path of the file is basePath+filePath
* @param filename Upload to FTP File name on the server
* @param input Input stream
* @return Successfully returns true, Otherwise return to false
*/
public static boolean uploadFile(String host, int port, String username, String password, String basePath,
String filePath, String filename, InputStream input) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);// Connect FTP The server
// If you use the default port , have access to ftp.connect(host) The way to connect directly to FTP The server
ftp.login(username, password);// Sign in
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
// Switch to upload directory
if (!ftp.changeWorkingDirectory(basePath + filePath)) {
// If the directory does not exist, create a directory
String[] dirs = filePath.split("/");
String tempPath = basePath;
for (String dir : dirs) {
if (null == dir || "".equals(dir)) continue;
tempPath += "/" + dir;
if (!ftp.changeWorkingDirectory(tempPath)) {
if (!ftp.makeDirectory(tempPath)) {
return result;
} else {
ftp.changeWorkingDirectory(tempPath);
}
}
}
}
// Set the upload file type to binary type
ftp.setFileType(FTP.BINARY_FILE_TYPE);
// Upload files
if (!ftp.storeFile(filename, input)) {
return result;
}
input.close();
ftp.logout();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
/**
* Description: from FTP Server downloads files
*
* @param host FTP The server hostname
* @param port FTP Server port
* @param username FTP Login account
* @param password FTP The login password
* @param remotePath FTP The relative path on the server
* @param fileName The name of the file to download
* @param localPath Download and save to local path
* @return Successfully returns true, Otherwise return to false
*/
public static boolean downloadFile(String host, int port, String username, String password, String remotePath,
String fileName, String localPath) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);
// If you use the default port , have access to ftp.connect(host) The way to connect directly to FTP The server
ftp.login(username, password);// Sign in
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
ftp.changeWorkingDirectory(remotePath);// Transferred to the FTP Server directory
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
File localFile = new File(localPath + "/" + ff.getName());
OutputStream is = new FileOutputStream(localFile);
ftp.retrieveFile(ff.getName(), is);
is.close();
}
}
ftp.logout();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
}
ftp The configuration file
GlobalConfig .java
package com.laolang.jd.core.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* Global configuration
*
* @author laolang
* @version 1.0
* 2019/1/5 17:36
*/
@Component
public class GlobalConfig {
@Value("${sys.author}")
private String sysAuthor;
@Value("${ftp.address}")
private String ftpAddress;
@Value("${ftp.port}")
private Integer ftpPort;
@Value("${ftp.userName}")
private String ftpUserName;
@Value("${ftp.passWord}")
private String ftpPassWord;
@Value("${ftp.basePath}")
private String ftpBasePath;
@Value("${ftp.imageBasePath}")
private String ftpImageBasePath;
public String getFtpAddress() {
return ftpAddress;
}
public void setFtpAddress(String ftpAddress) {
this.ftpAddress = ftpAddress;
}
public Integer getFtpPort() {
return ftpPort;
}
public void setFtpPort(Integer ftpPort) {
this.ftpPort = ftpPort;
}
public String getFtpUserName() {
return ftpUserName;
}
public void setFtpUserName(String ftpUserName) {
this.ftpUserName = ftpUserName;
}
public String getFtpPassWord() {
return ftpPassWord;
}
public void setFtpPassWord(String ftpPassWord) {
this.ftpPassWord = ftpPassWord;
}
public String getFtpBasePath() {
return ftpBasePath;
}
public void setFtpBasePath(String ftpBasePath) {
this.ftpBasePath = ftpBasePath;
}
public String getFtpImageBasePath() {
return ftpImageBasePath;
}
public void setFtpImageBasePath(String ftpImageBasePath) {
this.ftpImageBasePath = ftpImageBasePath;
}
public String getSysAuthor() {
return sysAuthor;
}
public void setSysAuthor(String sysAuthor) {
this.sysAuthor = sysAuthor;
}
}
# The system configuration
sys.author=khl
sys.adminPath=/admin
cache.redis.host=127.0.0.1
cache.redis.port=6379
#ftp To configure
ftp.address=127.0.0.1
ftp.port=21
ftp.userName=jdhello
ftp.passWord=jdhello
ftp.basePath=/upload
ftp.imageBasePath=http://image.jdhello.com/upload
service
PicUploadService
package com.laolang.jd.modules.admin.service;
import com.laolang.jd.common.util.FtpUtil;
import com.laolang.jd.common.util.IDUtils;
import com.laolang.jd.core.config.GlobalConfig;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* File upload service
*
* @author laolang
* @version 1.0
* 2019/1/8 02:18
*/
@Service
public class PicUploadService {
@Autowired
private GlobalConfig globalConfig;
public String uploadPic(MultipartFile multipartFile) {
if (null == multipartFile || multipartFile.isEmpty()) {
return null;
}
String originalFilename = multipartFile.getOriginalFilename();
String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
String imageName = IDUtils.genImageName();
DateTime dateTime = new DateTime();
String filePath = dateTime.toString("/yyyy/MM/dd");
try {
FtpUtil.uploadFile(globalConfig.getFtpAddress(),globalConfig.getFtpPort(),globalConfig.getFtpUserName(),globalConfig.getFtpPassWord(),
globalConfig.getFtpBasePath(),filePath,imageName+ext,multipartFile.getInputStream());
} catch (IOException e) {
e.printStackTrace();
return null;
}
return globalConfig.getFtpImageBasePath() + filePath + "/" + imageName + ext;
}
}
Controller
package com.laolang.jd.modules.admin.web;
import com.laolang.jd.common.json.PrintJsonWriter;
import com.laolang.jd.common.json.SimpleAjax;
import com.laolang.jd.common.util.GsonUtil;
import com.laolang.jd.common.util.StringUtils;
import com.laolang.jd.modules.admin.service.PicUploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* ftp
*
* @author laolang
* @version 1.0
* 2019/1/8 02:22
*/
@RequestMapping("/admin/ftp")
@Controller
public class AdminFtpController {
@Autowired
private PicUploadService picUploadService;
/**
* ueditor Image upload
* @param multipartFile
* @param response
*/
@RequestMapping(value = "/ueditorupload",method = RequestMethod.POST)
public void ueditorUpload(@RequestParam("file") MultipartFile multipartFile , HttpServletResponse response){
Map<String,Object> map = new HashMap<>();
try {
String url = picUploadService.uploadPic(multipartFile);
if(StringUtils.isNotBlank(url)){
map.put("state", "SUCCESS");// UEDITOR The rules of : Not for SUCCESS Is displayed state The content of
map.put("url",url); // Access to your current picture
map.put("title","");
map.put("original","realName" );
}else{
map.put("state", " File upload failed !");
map.put("url","");
map.put("title", "");
map.put("original", "");
}
String json = GsonUtil.toJson(map);
PrintJsonWriter.printJson(response,json);
} catch (Exception e) {
e.printStackTrace();
}
}
}
ueditor To configure
config.json
/* Front and rear communication related configuration , Comments are only allowed in multiline mode */
{
/* Upload image configuration item */
"imageActionName": "uploadimage", /* Perform the upload of pictures action name */
"imageFieldName": "file", /* The name of the submitted image form */
"imageMaxSize": 2048000, /* Upload size limit , Company B */
"imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* Upload image format display */
"imageCompressEnable": true, /* Whether to compress the picture , The default is true */
"imageCompressBorder": 1600, /* Image compression maximum edge limit */
"imageInsertAlign": "none", /* Floating mode of inserted picture */
"imageUrlPrefix": "", /* Image access path prefix */
"imagePathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* Upload save path , You can customize the save path and file name format */
/* {filename} Will be replaced with the original file name , When configuring this item, you need to pay attention to the problem of Chinese garbled code */
/* {rand:6} Will be replaced with random numbers , The number of digits following is a random number */
/* {time} Will be replaced by a timestamp */
/* {yyyy} Will be replaced with four digit years */
/* {yy} Will be replaced by two digit years */
/* {mm} Will be replaced by two digit months */
/* {dd} Will be replaced by two digit dates */
/* {hh} Will be replaced by two hours */
/* {ii} Will be replaced by two minutes */
/* {ss} Will be replaced by two seconds */
/* Illegal characters \ : * ? " < > | */
/* Please see the online documents for details : fex.baidu.com/ueditor/#use-format_upload_filename */
/* Graffiti picture upload configuration item */
"scrawlActionName": "uploadscrawl", /* Execute upload graffiti action name */
"scrawlFieldName": "upfile", /* The name of the submitted image form */
"scrawlPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* Upload save path , You can customize the save path and file name format */
"scrawlMaxSize": 2048000, /* Upload size limit , Company B */
"scrawlUrlPrefix": "", /* Image access path prefix */
"scrawlInsertAlign": "none",
/* Screenshot tool upload */
"snapscreenActionName": "uploadimage", /* Execute the... Of uploading screenshots action name */
"snapscreenPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* Upload save path , You can customize the save path and file name format */
"snapscreenUrlPrefix": "", /* Image access path prefix */
"snapscreenInsertAlign": "none", /* Floating mode of inserted picture */
/* Grab remote picture configuration */
"catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
"catcherActionName": "catchimage", /* Perform the task of capturing remote pictures action name */
"catcherFieldName": "source", /* The name of the submitted picture list form */
"catcherPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* Upload save path , You can customize the save path and file name format */
"catcherUrlPrefix": "", /* Image access path prefix */
"catcherMaxSize": 2048000, /* Upload size limit , Company B */
"catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* Capture picture format display */
/* Upload video configuration */
"videoActionName": "uploadvideo", /* Execute the of uploading video action name */
"videoFieldName": "upfile", /* Name of submitted video form */
"videoPathFormat": "/ueditor/jsp/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* Upload save path , You can customize the save path and file name format */
"videoUrlPrefix": "", /* Video access path prefix */
"videoMaxSize": 102400000, /* Upload size limit , Company B, Default 100MB */
"videoAllowFiles": [
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* Upload video format display */
/* Upload file configuration */
"fileActionName": "uploadfile", /* controller in , Execute the of uploading video action name */
"fileFieldName": "upfile", /* The name of the submitted file form */
"filePathFormat": "/ueditor/jsp/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* Upload save path , You can customize the save path and file name format */
"fileUrlPrefix": "", /* File access path prefix */
"fileMaxSize": 51200000, /* Upload size limit , Company B, Default 50MB */
"fileAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
], /* Upload file format display */
/* List the pictures in the specified directory */
"imageManagerActionName": "listimage", /* Perform image management action name */
"imageManagerListPath": "/ueditor/jsp/upload/image/", /* Specify the directory of pictures to list */
"imageManagerListSize": 20, /* List the number of documents each time */
"imageManagerUrlPrefix": "", /* Image access path prefix */
"imageManagerInsertAlign": "none", /* Floating mode of inserted picture */
"imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* File types listed */
/* List the files in the specified directory */
"fileManagerActionName": "listfile", /* Implementation of document management action name */
"fileManagerListPath": "/ueditor/jsp/upload/file/", /* Specify the directory to list the files */
"fileManagerUrlPrefix": "", /* File access path prefix */
"fileManagerListSize": 20, /* List the number of documents each time */
"fileManagerAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
] /* File types listed */
}
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/WEB-INF/jsp/common/common.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>jd Background management </title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
</head>
<body>
<script id="ueditor" name="content" type="text/plain"></script>
<!-- jQuery 2.2.3 -->
<script src="${ctx}/assets/adminlteiframe/plugins/jQuery/jquery-2.2.3.min.js"></script>
<!-- layui -->
<script src="${ctx}/assets/layui-2.4.5/layui.js"></script>
<!-- ztree -->
<script type="text/javascript" src="${ctx}/assets/zTree_v3-3.5.37/js/jquery.ztree.all.js"></script>
<!-- ueditor -->
<script type="text/javascript" src="${ctx}/assets/ueditor/ueditor.config.js"></script>
<!-- ueditor -->
<script type="text/javascript" src="${ctx}/assets/ueditor/ueditor.all.js"></script>
<!-- jd -->
<script src="${ctx}/assets/js/jd.js"></script>
<script src="${ctx}/assets/js/modules/admin/goodslist.js"></script>
</body>
</html>
js
// ueditor
UE.Editor.prototype._bkGetActionUrl = UE.Editor.prototype.getActionUrl;
UE.Editor.prototype.getActionUrl = function (action) {
if ('uploadimage' === action || 'uploadscrawl' === action || 'uploadvideo' === action) {
return jd.getBaseUrl() + '/admin/ftp/ueditorupload';
} else {
return this._bkGetActionUrl.call(this, action);
}
};
var ue = UE.getEditor('ueditor', {
serverUrl: jd.getBaseUrl() + '/ueditorconfig'
// imageActionName : jd.getBaseUrl() + '/admin/ftp/upload'
});
jd.getBaseUrl
/**
* obtain basePath
* Such as :http://localhost:8080
* @returns {string}
*/
getBaseUrl() {
// document.location.host // Indicates the current domain name + Port number
// document.location.hostname // Means domain name
// document.location.href // It means complete URL
// document.location.port // Represents the port number
// document.location.protocol // Indicates the current network protocol
return document.location.protocol + '//' + document.location.host;
}
matters needing attention
Access profile
- spring mvc Configuration file No 27 That's ok
because mvc All requests were intercepted , therefore ueditor Of controller.jsp cannot access , The following configuration is added to access ueditor Of controller.jsp
<!-- ueditor -->
<mvc:view-controller path="/ueditorconfig" view-name="../../assets/ueditor/jsp/controller"/>
- js The first 13 That's ok
Specify the path to access the configuration
config.json
The first 5 That's ok , Appoint The picture form name , That is to say :
ueditor js The order of introduction cannot be changed
···
···
done
边栏推荐
- haas506 2.0開發教程-高級組件庫-modem.sms(僅支持2.2以上版本)
- 直播带货这么火,如何在小程序中实现视频通话及直播互动功能?
- Haas506 2.0 development tutorial - Advanced Component Library -modem SMS (only supports versions above 2.2)
- Sword finger offer 42 Maximum sum of successive subarrays
- Copy and paste of idea without escape
- Open source ecology 𞓜 super practical open source license basic knowledge literacy post (Part 2)
- js中if逻辑过多,常见优化
- C language removes line breaks (or other characters) at the end of strings
- haas506 2.0开发教程-高级组件库-modem.sim(仅支持2.2以上版本)
- XShell7 下载
猜你喜欢

Miscellaneous things

Programmers' real ideas | daily anecdotes

Cloud box is deeply convinced to create a smart dual-mode teaching resource sharing platform for Nanjing No. 1 middle school

MySQL ON DUPLICATE KEY 和 PgSQL ON CONFLICT(主键) 处理主键冲突

Understand how learning JSX works

数据在内存中的存储方式(C语言)

如何查看本机IP

Copy and paste of idea without escape

JS to create an array (all elements are objects)

直播带货这么火,如何在小程序中实现视频通话及直播互动功能?
随机推荐
JS to create an array (all elements are objects)
C# 获取DPI和真实分辨率的方式(可以解决一直是96的问题)
haas506 2.0開發教程-高級組件庫-modem.sms(僅支持2.2以上版本)
Haas 506 2.0 Tutoriel de développement - bibliothèque de composants avancés - modem. SMS (ne prend en charge que les versions supérieures à 2,2)
Usage Summary of item views and item widgets controls in QT
2.17 haas506 2.0 development tutorial system (only versions above 2.2 are supported)
JSON to proto
mongodb 记录
idea的去除转义的复制粘贴
Easy EDA learning notes 09 esp32-wroom-32e module esp32-devkitc-v4 development board one click download circuit
Haas506 2.0 development tutorial -sntp (only versions above 2.2 are supported)
How to maintain secure encryption of email communication with FDA?
2.17 haas506 2.0开发教程-system(仅支持2.2以上版本)
Haas506 2.0 development tutorial - Advanced Component Library -modem Net (only supports versions above 2.2)
mysql 优化
XShell7 下载
Drawing and resetting of mars3d point, line and surface
Open source ecology 𞓜 super practical open source license basic knowledge literacy post (Part 2)
2022年养老理财产品有哪些?风险小的
反鸡汤致辞