add-img-store

This commit is contained in:
unknown
2025-06-11 16:01:13 +08:00
parent 9b5de6b912
commit 1dc9320ad7
5 changed files with 207 additions and 2 deletions

View File

@@ -122,8 +122,11 @@
<h1 id="title_phase2" class="h3 mb-0 text-gray-800" style="display:inline-block;font-weight:bold;font-size:1.25rem;"></h1> <h1 id="title_phase2" class="h3 mb-0 text-gray-800" style="display:inline-block;font-weight:bold;font-size:1.25rem;"></h1>
</div> </div>
<div>
<span style="font-size:0.8rem;color:#858796;cursor:pointer" class="lang" key="CONNECTINGSERVER" id="connect_span" onclick="check_link()" hidden><i class="fa fa-spinner fa-spin" style="margin-right:3px"></i></span> <span style="font-size:0.8rem;color:#858796;cursor:pointer" class="lang" key="CONNECTINGSERVER" id="connect_span" onclick="check_link()" hidden><i class="fa fa-spinner fa-spin" style="margin-right:3px"></i></span>
</div> <span style="font-size:0.8rem;color:#36b9cc;cursor:pointer;margin-left:5px" id="storage_space" onclick="open_storage()" hidden><i class="fa fa-database" style="margin-right:3px"></i>存储空间</span>
</div>
</div>
</div> </div>
<a href="//mixly.org" target="_blank" class="d-none d-md-block" style="margin-right:30px;color:gray;font-size:0.7rem;user-select:none"> <a href="//mixly.org" target="_blank" class="d-none d-md-block" style="margin-right:30px;color:gray;font-size:0.7rem;user-select:none">
Copyright© Mixly Team @ BNU, CHINA Copyright© Mixly Team @ BNU, CHINA

1
icons/store.svg Normal file
View File

@@ -0,0 +1 @@
<svg t="1749628239598" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4207" width="200" height="200"><path d="M511.875 128.125c-211.875 0-383.75 76.25-383.75 170.625 0 94.375 171.875 170.625 383.75 170.625 211.875 0 383.75-76.25 383.75-170.625C896.25 204.375 724.375 128.125 511.875 128.125M128.125 383.75l0 128.125c0 94.375 171.875 170.625 383.75 170.625 211.875 0 383.75-76.25 383.75-170.625l0-128.125c0 94.375-171.875 170.625-383.75 170.625C300 555 128.125 478.125 128.125 383.75M128.125 597.5l0 128.125c0 94.375 171.875 170.625 383.75 170.625 211.875 0 383.75-76.25 383.75-170.625l0-128.125c0 94.375-171.875 170.625-383.75 170.625C300 768.125 128.125 691.875 128.125 597.5z" p-id="4208" fill="#4e73df"></path></svg>

After

Width:  |  Height:  |  Size: 764 B

8
img/store/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
*.txt
*.png
*.bmp
*.jpg
*.jpeg
*.gif
*.svg
*.ico

View File

@@ -809,6 +809,7 @@ function view_project(projectName, projectType) {
$("#accordionSidebar").remove() $("#accordionSidebar").remove()
$("#sidebarToggleTop").remove() $("#sidebarToggleTop").remove()
$("#connect_span").removeAttr("hidden") $("#connect_span").removeAttr("hidden")
$("#storage_space").removeAttr("hidden")
init_layout() init_layout()
$("#projMode").click(function() { $("#projMode").click(function() {
if (globalProjectType != PROJ_MODE) { if (globalProjectType != PROJ_MODE) {
@@ -4202,4 +4203,117 @@ setInterval(function(){
save_layout(false, true) save_layout(false, true)
isChanged = false isChanged = false
} }
}, 30000) }, 30000)
function open_storage(){
var editForm = $('<div class="nnt" style="width:80vw;height:80vh;display:flex;flex-direction:column"/>')
editForm.append($('<div style="margin-top:-63px;margin-left:calc(40vw - 43px);margin-bottom:15px;box-shadow: 1px 1px 20px #4e73df;background-color:white;width:75px;height:75px;padding:40px;border-radius:80px;border:solid #4e73df 3px;display:flex;align-items:center;justify-content:center"><img src="icons/store.svg" style="width:45px;"></div>'))
editForm.append($('<h3 style="text-align:center;margin-bottom:5px">所有发送到<span style="color:#4e73df;font-weight:bold">/storage</span>主题下的消息和图片会被自动保存</h3>'))
var messagesContainer = $('<div style="flex:1;overflow-y:auto;padding:20px;display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));gap:15px;align-content:flex-start"/>')
var sync_stor = function(){
messagesContainer.empty()
var sampleMessages = [
]
$.getJSON('getImgStore', {
'projectName': globalProjectName
}, function(res) {
if(res.length==0)
{
messagesContainer.append($('<div style="background:white;border-radius:8px;padding:15px;box-shadow:0 2px 5px rgba(0,0,0,0.1);height:150px;display:flex;flex-direction:column;align-items:center;justify-content:center">暂无存储</div>'))
}
for (let ri = 0; ri < res.length; ri++) {
{
let url = "img/store/" + globalUserName + "/" + globalProjectName + "/" + res[ri]
if (url.endsWith('.txt')) {
sampleMessages.push({ type: 'text', content: url })
} else{
sampleMessages.push({ type: 'image', content: url })
}
}
}
for(let ji = 0; ji < sampleMessages.length; ji++) {
let msg = sampleMessages[ji]
let messageBox = $('<div style="background:white;border-radius:8px;padding:15px;box-shadow:0 2px 5px rgba(0,0,0,0.1);height:150px;display:flex;flex-direction:column"/>')
if (msg.type === 'text') {
$.ajax({
url: msg.content,
success: function(res) {
messageBox.append($('<div style="font-size:14px;word-break:break-all;height:100px"/>').text(res))
// Add timestamp (sample)
let timeStamp = parseInt(msg.content.split("/")[msg.content.split("/").length - 1].split(".")[0])
let timeString = new Date(timeStamp).toLocaleString()
let btdiv = $('<div style="font-size:11px;color:#999;margin-top:10px;text-align:right"></div>')
btdiv.append("<span>" + timeString +"</span>")
let deletebtn = $('<a class="btn btn-danger btn-sm" style="margin-left:10px"><i class="fa fa-trash"></i></a>')
deletebtn.click(function(){
$.getJSON('deleteImgStore', {
'projectName': globalProjectName,
'filename': msg.content.split("/")[msg.content.split("/").length - 1]
}, function(){
sync_stor()
})
})
btdiv.append(deletebtn)
messageBox.append(btdiv)
}
})
} else if (msg.type === 'image') {
messageBox.append($('<div style="width:100%;height:100px;text-align:center;overflow:hidden"/><img src="' + msg.content + '" style="max-width:100%;max-height:100px;object-fit:contain;margin:auto"/></div>'))
// Add timestamp (sample)
let timeStamp = parseInt(msg.content.split("/")[msg.content.split("/").length - 1].split(".")[0])
let timeString = new Date(timeStamp).toLocaleString()
let btdiv = $('<div style="font-size:11px;color:#999;margin-top:10px;text-align:right"></div>')
btdiv.append("<span>" + timeString +"</span>")
let viewbtn = $('<a class="btn btn-primary btn-sm" style="margin-left:10px"><i class="fa fa-eye"></i></a>')
viewbtn.click(function(){
// 全屏显示图片,点击任意退出
let fullDialog = dialog({
content: $('<div style="width:60vw;height:60vh;display:flex;align-items:center;justify-content:center"><img src="' + msg.content + '" style="max-width:100%;max-height:100%"/></div>')[0],
cancel: true,
cancelValue: '关闭'
})
fullDialog.showModal()
})
let deletebtn = $('<a class="btn btn-danger btn-sm" style="margin-left:10px"><i class="fa fa-trash"></i></a>')
deletebtn.click(function(){
$.getJSON('deleteImgStore', {
'projectName': globalProjectName,
'filename': msg.content.split("/")[msg.content.split("/").length - 1]
}, function(){
sync_stor()
})
})
btdiv.append(viewbtn)
btdiv.append(deletebtn)
messageBox.append(btdiv)
}
messagesContainer.prepend(messageBox)
}
})
}
editForm.append(messagesContainer)
sync_stor()
client.on('message', function(topic, msg) {
if(topic.split("/")[2] == "storage")
sync_stor()
})
// Bottom section
var bottomDiv = $('<div style="width:100%;margin-top:15px;display:flex;flex-direction:row;align-items:center;justify-content:space-around"/>')
var cancelEdit = $('<a class="btn btn-danger btn-circle" style="box-shadow:1px 1px 5px #e74a3b"><i class="fa fa-arrow-left"></i></a>')
bottomDiv.append(cancelEdit)
editForm.append(bottomDiv)
var modifyDia = dialog({
content: editForm[0],
cancel: false
})
cancelEdit.click(function() {
modifyDia.close().remove()
})
modifyDia.showModal()
}

View File

@@ -830,6 +830,30 @@ var mixioServer = async function() {
var topic = packet.topic.split('/') var topic = packet.topic.split('/')
var payload = String(packet.payload) var payload = String(packet.payload)
if (topic.length == 3) { if (topic.length == 3) {
if(topic[2] == 'storage') {
// 判断是否是base64, 开头为data:image/***;base64, ***可以为png,bmp,jpg,jpeg,gif,svg,ico
const allowFormats = ['png', 'bmp', 'jpg', 'jpeg', 'gif', 'svg', 'ico'];
const base64Reg = /^data:image\/(\w+);base64,/;
const match = payload.match(base64Reg);
if (match && allowFormats.includes(match[1])) {
// 是base64
const format = match[1];
const timeStamp = Date.now();
const fileName = `${timeStamp}.${format}`;
const filePath = path.join(__dirname, 'img', 'store', topic[0], topic[1], fileName);
const base64Data = payload.replace(base64Reg, '');
const buffer = Buffer.from(base64Data, 'base64');
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, buffer);
} else {
// 全部明文存为txt
const timeStamp = Date.now();
const fileName = `${timeStamp}.txt`;
const filePath = path.join(__dirname, 'img', 'store', topic[0], topic[1], fileName);
fs.mkdirSync(path.dirname(filePath), { recursive: true});
fs.writeFileSync(filePath, payload);
}
}
if (topic[2] == 'b640a0ce465fa2a4150c36b305c1c11b') { if (topic[2] == 'b640a0ce465fa2a4150c36b305c1c11b') {
if (STORAGE_ENGINE == "sqlite") if (STORAGE_ENGINE == "sqlite")
db.run("insert or ignore into devices (userName, clientid) values (?,?)", [topic[0], payload]) db.run("insert or ignore into devices (userName, clientid) values (?,?)", [topic[0], payload])
@@ -1323,6 +1347,61 @@ var mixioServer = async function() {
res.redirect('/') res.redirect('/')
}) })
app.get('/getImgStore', function(req, res) {
if (req.session.userName && req.query.projectName){
var projectName = req.query.projectName
// img/store/username/projectName
var imgStorePath = path.join(__dirname, 'img', 'store', req.session.userName, projectName)
// 文件名发送列表
fs.readdir(imgStorePath, function(err, files) {
res.send(files || [])
})
} else
res.redirect('/')
})
app.get('/clearImgStore', function(req, res) {
if (req.session.userName && req.query.projectName){
var projectName = req.query.projectName
// img/store/username/projectName
var imgStorePath = path.join(__dirname, 'img', 'store', req.session.userName, projectName)
// 清空文件
fs.readdir(imgStorePath, function(err, files) {
files.forEach(function(file) {
fs.unlink(path.join(imgStorePath, file), function(err) {
if (err) {
console.log(err)
}
})
})
})
res.send({
"status": "success"
})
} else
res.redirect('/')
})
app.get('/deleteImgStore', function(req, res) {
if (req.session.userName && req.query.projectName && req.query.filename){
var projectName = req.query.projectName
var filename = req.query.filename
// img/store/username/projectName
var imgStorePath = path.join(__dirname, 'img', 'store', req.session.userName, projectName)
// 删除文件
fs.unlink(path.join(imgStorePath, filename), function(err) {
if (err) {
console.log(err)
}
})
res.send({
"status": "success"
}
)
} else
res.redirect('/')
})
app.get('/projects', function(req, res) { app.get('/projects', function(req, res) {
if (req.session.userName) { if (req.session.userName) {
ejs.renderFile(__dirname + '/ejs/projects.ejs', { ejs.renderFile(__dirname + '/ejs/projects.ejs', {