works except for upload progress
This commit is contained in:
@@ -119,87 +119,6 @@ export function imageScaled(file, maxWidth, maxHeight, maxSize, forceSquare, onS
|
||||
img.src = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
// Convert uploaded image into a base64-encoded string possibly scaling
|
||||
// linear dimensions or constraining to a square.
|
||||
export function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onerror = function(err) {
|
||||
onError("Image format unrecognized");
|
||||
}
|
||||
img.onload = function() {
|
||||
var dim = fitImageSize(this.width, this.height, width, height, forceSquare);
|
||||
if (!dim) {
|
||||
onError("Invalid image");
|
||||
return;
|
||||
}
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = dim.dstWidth;
|
||||
canvas.height = dim.dstHeight;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.drawImage(this, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,
|
||||
0, 0, dim.dstWidth, dim.dstHeight);
|
||||
|
||||
var mime = (this.width != dim.dstWidth ||
|
||||
this.height != dim.dstHeight ||
|
||||
SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) ? 'image/jpeg' : file.type;
|
||||
var imageBits = canvas.toDataURL(mime);
|
||||
var parts = imageBits.split(',');
|
||||
// Get actual image type: 'data:image/png;base64,'
|
||||
mime = getMimeType(parts[0]);
|
||||
if (!mime) {
|
||||
onError("Unsupported image format");
|
||||
return;
|
||||
}
|
||||
// Ensure the image is not too large
|
||||
let quality = 0.78;
|
||||
if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) {
|
||||
mime = 'image/jpeg';
|
||||
}
|
||||
if (mime == 'image/jpeg') {
|
||||
// Reduce size of the jpeg by reducing image quality
|
||||
while (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE && quality > 0.45) {
|
||||
imageBits = canvas.toDataURL(mime, quality);
|
||||
quality *= 0.84;
|
||||
}
|
||||
}
|
||||
if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) {
|
||||
onError("The image size " + bytesToHumanSize(base64DecodedLen(imageBits.length)) +
|
||||
" exceeds the " + bytesToHumanSize(MAX_INBAND_ATTACHMENT_SIZE) + " limit.", "err");
|
||||
return;
|
||||
}
|
||||
canvas = null;
|
||||
onSuccess(imageBits.split(',')[1], mime, dim.dstWidth, dim.dstHeight, fileNameForMime(file.name, mime));
|
||||
};
|
||||
img.src = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
// Convert uploaded image file to base64-encoded string without scaling/converting the image
|
||||
export function imageFileToBase64(file, onSuccess, onError) {
|
||||
var reader = new FileReader();
|
||||
reader.addEventListener('load', function() {
|
||||
var parts = reader.result.split(',');
|
||||
var mime = getMimeType(parts[0]);
|
||||
if (!mime) {
|
||||
onError("Failed to process image file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get image size.
|
||||
var img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = function() {
|
||||
onSuccess(parts[1], mime, this.width, this.height, fileNameForMime(file.name, mime));
|
||||
}
|
||||
img.onerror = function(err) {
|
||||
onError("Image format not recognized");
|
||||
}
|
||||
img.src = URL.createObjectURL(file);
|
||||
}, false);
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
// Convert File to base64 string.
|
||||
export function fileToBase64(file, onSuccess) {
|
||||
const reader = new FileReader();
|
||||
|
||||
+7
-6
@@ -93,8 +93,8 @@ export function isUrlRelative(url) {
|
||||
|
||||
// Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of
|
||||
// strings with permitted URL schemes, such as ['ftp', 'ftps']; otherwise accept http and https only.
|
||||
// If apikey is defined, it's appended to relative URL.
|
||||
export function sanitizeUrl(url, apikey) {
|
||||
// If apikey and/or token is defined, it's appended to relative URL.
|
||||
export function sanitizeUrl(url) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
@@ -107,6 +107,7 @@ export function sanitizeUrl(url, apikey) {
|
||||
if (!/^([a-z][a-z0-9+.-]*:|\/\/)/i.test(url)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Blob URLs are safe.
|
||||
if (/^blob:http/.test(url)) {
|
||||
return url;
|
||||
@@ -123,15 +124,15 @@ export function sanitizeUrl(url, apikey) {
|
||||
}
|
||||
|
||||
// Ensure URL is suitable for <img src="url"> field: the URL must be a relative URL or
|
||||
// have http:, https:, or data: scheme.
|
||||
// The relative URL is appended with the API key.
|
||||
// have http:, https:, blob: or data: scheme.
|
||||
// The relative URL is appended with the API key and/or auth token.
|
||||
// In case of data: scheme, the URL must start with a 'data:image/XXXX;base64,'.
|
||||
export function sanitizeImageUrl(url, apikey) {
|
||||
export function sanitizeImageUrl(url) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sanitizedUrl = sanitizeUrl(url, apikey);
|
||||
const sanitizedUrl = sanitizeUrl(url);
|
||||
if (sanitizedUrl) {
|
||||
return sanitizedUrl;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import SendMessage from '../widgets/send-message.jsx';
|
||||
|
||||
import { DEFAULT_P2P_ACCESS_MODE, KEYPRESS_DELAY, MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE,
|
||||
MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE, READ_DELAY } from '../config.js';
|
||||
import { SUPPORTED_IMAGE_FORMATS, blobToBase64, filePasted, fileToBase64, imageFileToBase64,
|
||||
import { SUPPORTED_IMAGE_FORMATS, blobToBase64, filePasted, fileToBase64,
|
||||
imageScaled, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
import { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';
|
||||
@@ -559,7 +559,9 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
|
||||
handleClosePreview() {
|
||||
URL.revokeObjectURL(this.state.imagePreview.url);
|
||||
if (this.state.imagePreview && this.state.imagePreview.url) {
|
||||
URL.revokeObjectURL(this.state.imagePreview.url);
|
||||
}
|
||||
this.setState({ imagePostview: null, imagePreview: null, docPreview: null });
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ function draftyFormatter(style, data, values, key) {
|
||||
dim = dim || {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};
|
||||
attr.style = { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' };
|
||||
if (!uploading) {
|
||||
attr.src = sanitizeImageUrl(attr.src);
|
||||
attr.src = this.props.tinode.authorizeURL(sanitizeImageUrl(attr.src));
|
||||
if (attr.src) {
|
||||
attr.onClick = this.handleImagePreview;
|
||||
attr.className += ' image-clickable';
|
||||
|
||||
+7
-88
@@ -200,7 +200,7 @@ react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(react__WEBPACK_IMPORTED_
|
||||
/*!*********************************!*\
|
||||
!*** ./src/lib/blob-helpers.js ***!
|
||||
\*********************************/
|
||||
/*! exports provided: SUPPORTED_IMAGE_FORMATS, MIME_EXTENSIONS, makeImageUrl, fitImageSize, fileNameForMime, imageScaled, imageFileScaledToBase64, imageFileToBase64, fileToBase64, blobToBase64, filePasted, getMimeType, base64EncodedLen, base64DecodedLen, base64ReEncode */
|
||||
/*! exports provided: SUPPORTED_IMAGE_FORMATS, MIME_EXTENSIONS, makeImageUrl, fitImageSize, fileNameForMime, imageScaled, fileToBase64, blobToBase64, filePasted, getMimeType, base64EncodedLen, base64DecodedLen, base64ReEncode */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
@@ -211,8 +211,6 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fitImageSize", function() { return fitImageSize; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fileNameForMime", function() { return fileNameForMime; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "imageScaled", function() { return imageScaled; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "imageFileScaledToBase64", function() { return imageFileScaledToBase64; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "imageFileToBase64", function() { return imageFileToBase64; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fileToBase64", function() { return fileToBase64; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "blobToBase64", function() { return blobToBase64; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "filePasted", function() { return filePasted; });
|
||||
@@ -321,88 +319,6 @@ function imageScaled(file, maxWidth, maxHeight, maxSize, forceSquare, onSuccess,
|
||||
|
||||
img.src = URL.createObjectURL(file);
|
||||
}
|
||||
function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
|
||||
img.onerror = function (err) {
|
||||
onError("Image format unrecognized");
|
||||
};
|
||||
|
||||
img.onload = function () {
|
||||
var dim = fitImageSize(this.width, this.height, width, height, forceSquare);
|
||||
|
||||
if (!dim) {
|
||||
onError("Invalid image");
|
||||
return;
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = dim.dstWidth;
|
||||
canvas.height = dim.dstHeight;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.drawImage(this, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight, 0, 0, dim.dstWidth, dim.dstHeight);
|
||||
var mime = this.width != dim.dstWidth || this.height != dim.dstHeight || SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0 ? 'image/jpeg' : file.type;
|
||||
var imageBits = canvas.toDataURL(mime);
|
||||
var parts = imageBits.split(',');
|
||||
mime = getMimeType(parts[0]);
|
||||
|
||||
if (!mime) {
|
||||
onError("Unsupported image format");
|
||||
return;
|
||||
}
|
||||
|
||||
let quality = 0.78;
|
||||
|
||||
if (base64DecodedLen(imageBits.length) > _config_js__WEBPACK_IMPORTED_MODULE_0__["MAX_INBAND_ATTACHMENT_SIZE"]) {
|
||||
mime = 'image/jpeg';
|
||||
}
|
||||
|
||||
if (mime == 'image/jpeg') {
|
||||
while (base64DecodedLen(imageBits.length) > _config_js__WEBPACK_IMPORTED_MODULE_0__["MAX_INBAND_ATTACHMENT_SIZE"] && quality > 0.45) {
|
||||
imageBits = canvas.toDataURL(mime, quality);
|
||||
quality *= 0.84;
|
||||
}
|
||||
}
|
||||
|
||||
if (base64DecodedLen(imageBits.length) > _config_js__WEBPACK_IMPORTED_MODULE_0__["MAX_INBAND_ATTACHMENT_SIZE"]) {
|
||||
onError("The image size " + Object(_strformat_js__WEBPACK_IMPORTED_MODULE_1__["bytesToHumanSize"])(base64DecodedLen(imageBits.length)) + " exceeds the " + Object(_strformat_js__WEBPACK_IMPORTED_MODULE_1__["bytesToHumanSize"])(_config_js__WEBPACK_IMPORTED_MODULE_0__["MAX_INBAND_ATTACHMENT_SIZE"]) + " limit.", "err");
|
||||
return;
|
||||
}
|
||||
|
||||
canvas = null;
|
||||
onSuccess(imageBits.split(',')[1], mime, dim.dstWidth, dim.dstHeight, fileNameForMime(file.name, mime));
|
||||
};
|
||||
|
||||
img.src = URL.createObjectURL(file);
|
||||
}
|
||||
function imageFileToBase64(file, onSuccess, onError) {
|
||||
var reader = new FileReader();
|
||||
reader.addEventListener('load', function () {
|
||||
var parts = reader.result.split(',');
|
||||
var mime = getMimeType(parts[0]);
|
||||
|
||||
if (!mime) {
|
||||
onError("Failed to process image file");
|
||||
return;
|
||||
}
|
||||
|
||||
var img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
|
||||
img.onload = function () {
|
||||
onSuccess(parts[1], mime, this.width, this.height, fileNameForMime(file.name, mime));
|
||||
};
|
||||
|
||||
img.onerror = function (err) {
|
||||
onError("Image format not recognized");
|
||||
};
|
||||
|
||||
img.src = URL.createObjectURL(file);
|
||||
}, false);
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
function fileToBase64(file, onSuccess) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', function () {
|
||||
@@ -811,7 +727,7 @@ function asEmail(val) {
|
||||
function isUrlRelative(url) {
|
||||
return !/^\s*([a-z][a-z0-9+.-]*:|\/\/)/im.test(url);
|
||||
}
|
||||
function sanitizeUrl(url, allowedSchemes) {
|
||||
function sanitizeUrl(url) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
@@ -3481,7 +3397,10 @@ class MessagesView extends react__WEBPACK_IMPORTED_MODULE_0___default.a.Componen
|
||||
}
|
||||
|
||||
handleClosePreview() {
|
||||
URL.revokeObjectURL(this.state.imagePreview.url);
|
||||
if (this.state.imagePreview && this.state.imagePreview.url) {
|
||||
URL.revokeObjectURL(this.state.imagePreview.url);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
imagePostview: null,
|
||||
imagePreview: null,
|
||||
@@ -6751,7 +6670,7 @@ function draftyFormatter(style, data, values, key) {
|
||||
};
|
||||
|
||||
if (!uploading) {
|
||||
attr.src = Object(_lib_utils_js__WEBPACK_IMPORTED_MODULE_7__["sanitizeImageUrl"])(attr.src);
|
||||
attr.src = this.props.tinode.authorizeURL(Object(_lib_utils_js__WEBPACK_IMPORTED_MODULE_7__["sanitizeImageUrl"])(attr.src));
|
||||
|
||||
if (attr.src) {
|
||||
attr.onClick = this.handleImagePreview;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+58
-14
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user