-
-
Notifications
You must be signed in to change notification settings - Fork 423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added document tool to upload images #42
base: master
Are you sure you want to change the base?
Changes from all commits
e6a5639
6a4c9bd
4b1ee6e
9f210b3
38bebf3
e8ff46c
5247853
0a99ad2
061f84d
bc0e1e9
b1b875a
4e258c0
cd8a9ca
0067399
79352c9
8aefd41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
(function documents() { //Code isolation | ||
|
||
|
||
var xlinkNS = "http://www.w3.org/1999/xlink"; | ||
var imgCount = 1; | ||
|
||
function assert_count() { | ||
if (Tools.svg.querySelectorAll("image").length >= Tools.server_config.MAX_DOCUMENT_COUNT) { | ||
alert("Too many documents exist already"); | ||
throw new Error("Too many documents exist already"); | ||
} | ||
} | ||
|
||
function onstart() { | ||
var fileInput = document.createElement("input"); | ||
fileInput.type = "file"; | ||
fileInput.accept = "image/*"; | ||
fileInput.click(); | ||
fileInput.addEventListener("change", function () { | ||
assert_count(); | ||
|
||
var reader = new FileReader(); | ||
reader.readAsDataURL(fileInput.files[0]); | ||
|
||
reader.onload = function (e) { | ||
// use canvas to compress image | ||
var image = new Image(); | ||
image.src = e.target.result; | ||
image.onload = function () { | ||
|
||
assert_count(); | ||
|
||
var uid = Tools.generateUID("doc"); // doc for document | ||
|
||
var ctx, size; | ||
var scale = 1; | ||
|
||
do { | ||
// Todo give feedback of processing effort | ||
|
||
ctx = document.createElement("canvas").getContext("2d"); | ||
ctx.canvas.width = image.width * scale; | ||
ctx.canvas.height = image.height * scale; | ||
ctx.drawImage(image, 0, 0, image.width * scale, image.height * scale); | ||
var dataURL = ctx.canvas.toDataURL("image/webp", 0.7); | ||
|
||
// Compressed file size as data url, approximately 1/3 larger than as bytestream | ||
size = dataURL.length; | ||
|
||
// attempt again with an image that is at least 10% smaller | ||
scale = scale * Math.sqrt(Math.min( | ||
0.9, | ||
Tools.server_config.MAX_DOCUMENT_SIZE / size | ||
)); | ||
} while (size > Tools.server_config.MAX_DOCUMENT_SIZE); | ||
|
||
var msg = { | ||
id: uid, | ||
type: "doc", | ||
data: dataURL, | ||
size: size, | ||
w: this.width * scale || 300, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This uses the newly reduced value for |
||
h: this.height * scale || 300, | ||
x: (100 + document.documentElement.scrollLeft) / Tools.scale + 10 * imgCount, | ||
y: (100 + document.documentElement.scrollTop) / Tools.scale + 10 * imgCount | ||
//fileType: fileInput.files[0].type | ||
}; | ||
|
||
assert_count(); | ||
|
||
draw(msg); | ||
Tools.send(msg,"Document"); | ||
imgCount++; | ||
}; | ||
}; | ||
// Tools.change(Tools.prevToolName); | ||
}); | ||
} | ||
|
||
function draw(msg) { | ||
var aspect = msg.w/msg.h; | ||
var img = Tools.createSVGElement("image"); | ||
img.id=msg.id; | ||
img.setAttribute("class", "layer-"+Tools.layer); | ||
img.setAttributeNS(xlinkNS, "href", msg.data); | ||
img.x.baseVal.value = msg['x']; | ||
img.y.baseVal.value = msg['y']; | ||
img.setAttribute("width", 400*aspect); | ||
img.setAttribute("height", 400); | ||
Comment on lines
+88
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably have a maximum and a minimum image size, and scale only images that do not fit. |
||
Tools.drawingArea.appendChild(img); | ||
|
||
} | ||
|
||
Tools.add({ | ||
"name": "Document", | ||
//"shortcut": "", | ||
"draw": draw, | ||
"onstart": onstart, | ||
"oneTouch":true, | ||
"icon": "/tools/document/icon.svg", | ||
}); | ||
|
||
})(); //End of code isolation |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -231,7 +231,11 @@ BoardData.load = async function loadBoard(name) { | |
try { | ||
data = await fs.promises.readFile(boardData.file); | ||
boardData.board = JSON.parse(data); | ||
for (id in boardData.board) boardData.validate(boardData.board[id]); | ||
boardData.existingDocuments = 0; | ||
for (id in boardData.board) { | ||
boardData.validate(boardData.board[id]); | ||
if (boardData.board[id].type === "doc") existingDocuments += 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be |
||
} | ||
log('disk load', { 'board': boardData.name }); | ||
} catch (e) { | ||
log('empty board creation', { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,12 @@ module.exports = { | |
/** Maximum value for any x or y on the board */ | ||
MAX_BOARD_SIZE: parseInt(process.env['WBO_MAX_BOARD_SIZE']) || 65536, | ||
|
||
/** Maximum size of uploaded documents default 1MB */ | ||
MAX_DOCUMENT_SIZE: parseInt(process.env['WBO_MAX_DOCUMENT_SIZE']) || 1048576, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Large images failed for me, causing a transport error. The socket.io default maxHttpBufferSize is 1e6, so I changed this to 1e6 - 500. |
||
|
||
/** Maximum number of documents allowed */ | ||
MAX_DOCUMENT_COUNT: parseInt(process.env['WBO_MAX_DOCUMENT_COUNT']) || 5, | ||
|
||
/** Maximum messages per user over the given time period before banning them */ | ||
MAX_EMIT_COUNT: parseInt(process.env['WBO_MAX_EMIT_COUNT']) || 192, | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,7 @@ function socketConnection(socket) { | |
var board = await getBoard(name); | ||
board.users.add(socket.id); | ||
log('board joined', { 'board': board.name, 'users': board.users.size }); | ||
|
||
return board; | ||
} | ||
|
||
|
@@ -66,7 +67,7 @@ function socketConnection(socket) { | |
|
||
var lastEmitSecond = Date.now() / config.MAX_EMIT_COUNT_PERIOD | 0; | ||
var emitCount = 0; | ||
socket.on('broadcast', noFail(function onBroadcast(message) { | ||
socket.on('broadcast', noFail(async function onBroadcast(message) { | ||
var currentSecond = Date.now() / config.MAX_EMIT_COUNT_PERIOD | 0; | ||
if (currentSecond === lastEmitSecond) { | ||
emitCount++; | ||
|
@@ -96,10 +97,33 @@ function socketConnection(socket) { | |
return; | ||
} | ||
|
||
if (!message.data.tool || config.BLOCKED_TOOLS.includes(message.data.tool)) { | ||
if (!message.data.tool || config.BLOCKED_TOOLS.includes(message.data.tool)) { | ||
log('BLOCKED MESSAGE', message.data); | ||
return; | ||
} | ||
|
||
var boardData; | ||
if (message.data.type === "doc") { | ||
boardData = await getBoard(boardName); | ||
|
||
if (boardData.existingDocuments >= config.MAX_DOCUMENT_COUNT) { | ||
console.warn("Received too many documents"); | ||
return; | ||
} | ||
|
||
if (message.data.data.length > config.MAX_DOCUMENT_SIZE) { | ||
console.warn("Received too large file"); | ||
return; | ||
} | ||
|
||
boardData.existingDocuments += 1; | ||
} else if (message.data.type === "delete") { | ||
boardData = await getBoard(boardName); | ||
|
||
if (boardData.board[message.data.id].type === "doc") { | ||
boardData.existingDocuments -= 1; | ||
} | ||
} | ||
Comment on lines
+106
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be handled in the BoardData class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, we should also verify the provided URL, to check that it's a data URL. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we would still broadcast the large files to all users in that case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you are right, I hadn't thought about that. I still think the board-specific code should happen in BoardData, but we should probably set the |
||
|
||
// Save the message in the board | ||
handleMessage(boardName, data, socket); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably use jpeg by default here... We can make it configurable, though.
https://caniuse.com/#feat=webp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jpeg does not support transparency, which would require us to check what kind of image was uploaded and if it made use of the alpha channel.
If it is a png with transparency we would not be able to compress it other than by shrinking the dimensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest using a webp polyfill, e.g. https://github.com/chase-moskal/webp-hero
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's maybe start with JPEG, and then add support for webp, with a polyfill, in a second pull request ? webp-hero won't work as-is with data urls...