Using the Dropzone, the MIME type is automatically detected by the browser while uploading to Wasabi. If you have a need to override it, please revert back to me privately to better understand your use-case.
@redvivi I just installed the plugin and I’m doing some tests to see if it suits my needs. The dropzone uploader works fine, however, backend actions like getting file metadata and generating presigned URLs don’t work when an http referer access policy is assigned like shown below. I need to get rid of the policy for them to work. Any idea how to get around this? How do I alter the policy to allow the access needed for the backend actions? I don’t want it to be possible to open even short duration presigned URLs outside of the app which is why I need this policy. I know http referer is quite easy to get around but it’s still an extra layer of security.
HTTP referer is not the one you think in backend operations, as it is called from Bubble’s servers
You might want to allow only IP addresses used by Bubble’s clusters - see IP address range used by Bubble, or alternatively Bubble support.
I’m away from computer but I have it setup where I allowed a specific Wasabi user to have full S3 access, then the Bubble app uses that user’s API key. So the backend actions have the s3:PutObject and all other permissions needed.
Hi @redvivi,
Is the compression executed in the browser or by the server? I’m asking this because I’m currently facing issues with another Wasabi plugin, where my app crashes when too many and too large images get compressed since it uses too much computing power for the browser.
Thanks in advance.
Browser-side. It may crash because of the resulting compressed files, stored in memory, are exceeding the allowable limit of the browser’s JS.
All plugins on the marketplace using in-browser compression will suffer from this, unfortunately.
The solution is to limit the number of files or using a specialized service compressing assets on server side.
Currently experiencing problems, is this due to the Amazon/Bubble situation?
Yes it is.
@tylerboodman @redvivi Thanks for the input guys. I ended up exploring S3 permission definition in a bit more detail and found what works for my use case. My main use case is presigned url generation (based on S3 docs I thought the minimum duration was 1 minute but the plugin allows durations as short as 1 second which is great) and I needed to accomplish these goals:
- Prevent any access to my Wasabi bucket’s files apart from the cases described below
- Allow the Bubble plugin to access my Wasabi bucket
- Allow the files to be accessed from my website (e.g., for embedders or opening the links)
- Allow the Google Drive embedder to access the files
This is achieved by the following conditions:
- Allow the relevant IP addresses that Bubble uses in the backend which are listed on IP Ranges - Documentation - OctoPerf under US West (Oregon)
- Allow requests that use my domain as http referer
- Allow Google Drive embedder’s user agent
Note - none of these are bulletproof against someone who knows what they’re doing but they’re good enough to prevent unauthorised access and sharing of privilged resources by general users.
In case anyone is after a similar solution, below is my Wasabi bucket’s access policy that achieves the above. It denies access to the bucket’s files unless at least 1 of the above described conditions is satisfied. Keep in mind that it only allows the GetObject action due to my described use case but the policy can easily be adapted to include more actions.
{
"Id": "Policy1722457766584",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1722457618992",
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[BUCKET NAME]/*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"139.56.16.0/23",
[US West (Oregon) AWS IP addresses],
"99.77.186.0/24"
]
},
"StringNotLike": {
"aws:Referer": [
"https://[DOMAIN NAME]/",
"https://[DOMAIN NAME]/*",
"https://console.wasabisys.com/",
"https://console.wasabisys.com/*"
],
"aws:UserAgent": "Mozilla/5.0 (compatible; Google AppsViewer; http://drive.google.com)"
}
}
}
]
}
Also, a couple of notes on the plugin:
- It would be useful to have a specific error message when the plugin doesn’t gain the required access - it now gives a generic one so it can take a while to figure out what the cause is
- I think the guide is a bit outdated - for example, I don’t see a cross-origin resource sharing section in my Wasabi bucket permissions tab which is needed for step 1) of the instructions on the plugin page
Hi @redvivi,
I’m trying to show the thumbnail preview, but even with a one on one copy from your demo editor, it is not showing up. How can I solve this?
Thanks in advance.
Send me in DM your app editor with write access, specify the page where the uploader is installed, along with the sample file you are trying to preview.
Hi when i upload photos from safari at Iphone, and resize and compress photos idk why but they rotate. Checked it form desctop Chrome — all works good. Just as i know when we make resize procces at brouser side it can lose orientation, can you add some setting Do not Rotate etc.
Checked — it happend only with photos that i made on iphone…
@redvivi yes after all my testing — this problem is only with photos made on phone (in my case on Iphone) if for example i made a screenshot of photo it resize and upload normally, but if you take photo it turn on 90° and if you download this file and upload it again it make another turn on on 90°. But base64 not turning at 90° … but if you download this base and get jpg and upload it with resize it turn on 90° again… could you help please how to solve this problem?
I have identified the issue. The definitive fix requires quite some work and isn’t easy.
Basically you have the “natural” orientation of the image (e.g. the pixel’s bytes are presented). You read them and the image is displayed as it is, that’s one thing.
One of the other hand, photographic devices may store orientation information in EXIF data at capture, which is then used to display it accordingly on such devices, which may differ from the pixel bytes presentation.
Can you try v1.27.1 @sashashru1234 ? I have tried a long shot fix…
Hi @redvivi! Thank you for answer!
i’ve update plugin, not working. May be i don’t see some option that i should turn on?
And i tested. Photos that i upload via chorme from mac — they comressed and rezided but still turning at 90. Buttt from Iphone Safari — compression and resizing disabled i’ve tried it 6 times with differend photos, it’s upload just original size with normal orientation. But base64 working normaly in both devices
Here is example https://wnote.b-cdn.net/IMG_3665%20(1).jpeg
There is no option - I deactivated the rotation detection but obviously that’s not sufficient.
Hi @redvivi
I’ve talk a lot yesterday with chatgbt about that issue
It give me some solve option, i just share may be it could help to find rigth way.
EXIF.js set-up
<script src="https://cdn.jsdelivr.net/npm/exif-js"></script>
Var1
function handleFileUpload(file) {
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
// Создаем canvas для обработки изображения
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
EXIF.getData(img, function() {
const orientation = EXIF.getTag(this, "Orientation");
// Настройка canvas с правильной ориентацией
switch (orientation) {
case 2: // Flip horizontal
ctx.transform(-1, 0, 0, 1, canvas.width, 0);
break;
case 3: // Rotate 180 degrees
ctx.transform(-1, 0, 0, -1, canvas.width, canvas.height);
break;
case 4: // Flip vertical
ctx.transform(1, 0, 0, -1, 0, canvas.height);
break;
case 5: // Rotate 90 degrees counterclockwise
ctx.transform(0, 1, 1, 0, 0, 0);
break;
case 6: // Rotate 90 degrees clockwise
ctx.transform(0, 1, -1, 0, canvas.height, 0);
break;
case 7: // Rotate 270 degrees clockwise
ctx.transform(0, -1, -1, 0, canvas.height, canvas.width);
break;
case 8: // Rotate 270 degrees counterclockwise
ctx.transform(0, -1, 1, 0, 0, canvas.width);
break;
default: // No transformation needed
ctx.transform(1, 0, 0, 1, 0, 0);
}
// Рисуем изображение на canvas
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Преобразуем canvas в файл и передаем дальше
canvas.toBlob(function(blob) {
// Ваш код для загрузки файла
console.log(blob); // здесь можно продолжить с загрузкой изображения
}, 'image/jpeg', 0.7); // качество 0.7
});
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
var2
Dropzone.options.myDropzone = {
url: '/upload', // путь к серверу, на который вы отправляете изображения
maxFilesize: 10, // максимальный размер файла (в MB)
acceptedFiles: 'image/*', // только изображения
init: function() {
this.on("addedfile", function(file) {
// Проверка ориентации изображения при добавлении файла
fixOrientation(file, function(fixedFile) {
// После коррекции ориентации, заменяем файл в Dropzone
const dataTransfer = new DataTransfer();
dataTransfer.items.add(fixedFile);
file = dataTransfer.files[0];
this.emit('thumbnail', file, URL.createObjectURL(fixedFile)); // Генерация миниатюры
});
});
}
};
// Функция для обработки ориентации изображения
function fixOrientation(file, callback) {
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
// Создаем canvas для исправления ориентации
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// Читаем EXIF данные для ориентации
EXIF.getData(img, function() {
const orientation = EXIF.getTag(this, "Orientation");
let width = img.width;
let height = img.height;
// Обрезаем или ресайзим изображение, если оно больше нужных размеров
const maxWidth = 1280;
const maxHeight = 720;
if (width > maxWidth) {
height = Math.round((maxWidth / width) * height);
width = maxWidth;
}
if (height > maxHeight) {
width = Math.round((maxHeight / height) * width);
height = maxHeight;
}
canvas.width = width;
canvas.height = height;
// Применяем ориентацию
switch (orientation) {
case 2: // Flip horizontal
ctx.transform(-1, 0, 0, 1, canvas.width, 0);
break;
case 3: // Rotate 180 degrees
ctx.transform(-1, 0, 0, -1, canvas.width, canvas.height);
break;
case 4: // Flip vertical
ctx.transform(1, 0, 0, -1, 0, canvas.height);
break;
case 5: // Rotate 90 degrees counterclockwise
ctx.transform(0, 1, 1, 0, 0, 0);
break;
case 6: // Rotate 90 degrees clockwise
ctx.transform(0, 1, -1, 0, canvas.height, 0);
break;
case 7: // Rotate 270 degrees clockwise
ctx.transform(0, -1, -1, 0, canvas.height, canvas.width);
break;
case 8: // Rotate 270 degrees counterclockwise
ctx.transform(0, -1, 1, 0, 0, canvas.width);
break;
default:
ctx.transform(1, 0, 0, 1, 0, 0); // Без изменений
}
// Рисуем изображение на canvas
ctx.drawImage(img, 0, 0, width, height);
// Преобразуем canvas в изображение
canvas.toBlob(function(blob) {
callback(blob);
}, "image/jpeg", 0.7); // качество 0.7
});
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
}
var3
// Функция для обработки изображения
function processImage(file, maxWidth, maxHeight, quality, callback) {
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
// Создаем canvas для обработки
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// Получаем ориентацию изображения с помощью EXIF
EXIF.getData(img, function() {
const orientation = EXIF.getTag(this, "Orientation");
let width = img.width;
let height = img.height;
// Масштабируем изображение, если оно превышает максимальные размеры
if (width > maxWidth) {
height = Math.round((maxWidth / width) * height);
width = maxWidth;
}
if (height > maxHeight) {
width = Math.round((maxHeight / height) * width);
height = maxHeight;
}
// Настроим canvas для рисования с правильной ориентацией
canvas.width = width;
canvas.height = height;
// Учитываем ориентацию, чтобы изображение не переворачивалось
switch (orientation) {
case 2: // Flipped horizontally
ctx.transform(-1, 0, 0, 1, canvas.width, 0);
break;
case 3: // 180 degrees
ctx.transform(-1, 0, 0, -1, canvas.width, canvas.height);
break;
case 4: // Flipped vertically
ctx.transform(1, 0, 0, -1, 0, canvas.height);
break;
case 5: // 90 degrees rotated
ctx.transform(0, 1, 1, 0, 0, 0);
break;
case 6: // 90 degrees rotated
ctx.transform(0, 1, -1, 0, canvas.height, 0);
break;
case 7: // 270 degrees rotated
ctx.transform(0, -1, -1, 0, canvas.height, canvas.width);
break;
case 8: // 270 degrees rotated
ctx.transform(0, -1, 1, 0, 0, canvas.width);
break;
default:
ctx.transform(1, 0, 0, 1, 0, 0);
}
// Рисуем изображение на canvas с учетом ориентации
ctx.drawImage(img, 0, 0, width, height);
// Получаем изображение с качеством 0.7
canvas.toBlob(function(blob) {
callback(blob);
}, "image/jpeg", quality);
});
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
}
// Пример использования
// Получаем файл (например, через Dropzone)
var file = event.target.files[0];
// Максимальные размеры и качество
const maxWidth = 1280;
const maxHeight = 720;
const quality = 0.7;
// Вызовем функцию обработки изображения
processImage(file, maxWidth, maxHeight, quality, function(processedFile) {
console.log("Обработанный файл готов:", processedFile);
// Добавляем обработанный файл в Dropzone или другой элемент
dropzoneInstance.addFile(processedFile);
});
I am not software dude, just interested in this function works. Thank you
My guy (or gal), no offense, but @redvivi has the majority of the AI plugins on the market. I’m sure he’s tried to use AI to help in this situation.
When it comes to AI helping you with a solution, or finding a solution, it can help you from point A to point Z, sure. But when it comes to point C, D, G, O, Q…. That’s when things get fuzzy. It’s not a simple “hey, this is giving me this issue, how can it be fixed?”. The AI doesn’t understand how each of his functions works, how Bubble works, and how the libraries work, all in a simple prompt or even with some context. Give him time, and I’m sure he’ll find a probable solution.
If you feel comfortable with AI developing plugins for you, you should just give it a try on your own
Of course, I’m not rushing, I’m just trying to be helpful
I have good news)
@redvivi fixed it and now when you want to upload a photo from your phone, nothing will be reversed and the compression will work perfectly! I uploaded 6 and 12 photos at a time, everything is fine
Thank you for your work