diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs index 2e002f41..04aa098f 100644 --- a/Dotnet/AppApi/AppApi.cs +++ b/Dotnet/AppApi/AppApi.cs @@ -60,6 +60,65 @@ namespace VRCX } } + public string ResizeImageToFitLimits(string base64data) + { + return Convert.ToBase64String(ResizeImageToFitLimits(Convert.FromBase64String(base64data))); + } + + public byte[] ResizeImageToFitLimits(byte[] imageData, int maxWidth = 2000, int maxHeight = 2000, long maxSize = 10_000_000) + { + using var fileMemoryStream = new MemoryStream(imageData); + System.Drawing.Bitmap image = new System.Drawing.Bitmap(fileMemoryStream); + if (image.Width > maxWidth) + { + var sizingFactor = image.Width / (double)maxWidth; + int newHeight = (int)Math.Round(image.Height / sizingFactor); + image = new System.Drawing.Bitmap(image, maxWidth, newHeight); + } + if (image.Height > maxHeight) + { + var sizingFactor = image.Height / (double)maxHeight; + int newWidth = (int)Math.Round(image.Width / sizingFactor); + image = new System.Drawing.Bitmap(image, newWidth, maxHeight); + } + + void saveToFileToUpload() + { + using var imageSaveMemoryStream = new MemoryStream(); + image.Save(imageSaveMemoryStream, System.Drawing.Imaging.ImageFormat.Png); + imageData = imageSaveMemoryStream.ToArray(); + } + + saveToFileToUpload(); + + for (int i = 0; i < 250 && imageData.Length > maxSize; i++) + { + saveToFileToUpload(); + if (imageData.Length < maxSize) + break; + int newWidth = image.Width; + int newHeight = image.Height; + if (image.Width > image.Height) + { + newWidth = image.Width - 25; + newHeight = (int)Math.Round(image.Height / (image.Width / (double)newWidth)); + } + else + { + newHeight = image.Height - 25; + newWidth = (int)Math.Round(image.Width / (image.Height / (double)newHeight)); + } + image = new System.Drawing.Bitmap(image, newWidth, newHeight); + } + + if (imageData.Length > maxSize) + { + throw new Exception("Failed to get image into target filesize."); + } + + return imageData; + } + /// /// Computes the signature of the file represented by the specified base64-encoded string using the librsync library. /// diff --git a/Dotnet/WebApi.cs b/Dotnet/WebApi.cs index e627d32c..b4f6360a 100644 --- a/Dotnet/WebApi.cs +++ b/Dotnet/WebApi.cs @@ -217,7 +217,8 @@ namespace VRCX } } var imageData = options["imageData"] as string; - byte[] fileToUpload = Convert.FromBase64CharArray(imageData.ToCharArray(), 0, imageData.Length); + byte[] fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData)); + string fileFormKey = "file"; string fileName = "blob"; string fileMimeType = "image/png"; diff --git a/html/src/app.js b/html/src/app.js index bef3057e..456fb069 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -21598,8 +21598,8 @@ speechSynthesis.getVoices(); if (!files.length) { return; } - if (files[0].size >= 10000000) { - // 10MB + if (files[0].size >= 100000000) { + // 100MB $app.$message({ message: 'File size too large', type: 'error' @@ -21665,8 +21665,8 @@ speechSynthesis.getVoices(); if (!files.length) { return; } - if (files[0].size >= 10000000) { - // 10MB + if (files[0].size >= 100000000) { + // 100MB $app.$message({ message: 'File size too large', type: 'error' @@ -21674,7 +21674,7 @@ speechSynthesis.getVoices(); this.clearInviteImageUpload(); return; } - if (!files[0].type.match(/image.png/)) { + if (!files[0].type.match(/image.*/)) { $app.$message({ message: "File isn't a png", type: 'error' @@ -22679,6 +22679,11 @@ speechSynthesis.getVoices(); return response; }; + $app.methods.resizeImageToFitLimits = async function (file) { + var response = await AppApi.ResizeImageToFitLimits(file); + return response; + }; + $app.methods.genSig = async function (file) { var response = await AppApi.SignFile(file); return response; @@ -22706,8 +22711,8 @@ speechSynthesis.getVoices(); clearFile(); return; } - if (files[0].size >= 10000000) { - // 10MB + if (files[0].size >= 100000000) { + // 100MB $app.$message({ message: 'File size too large', type: 'error' @@ -22715,7 +22720,7 @@ speechSynthesis.getVoices(); clearFile(); return; } - if (!files[0].type.match(/image.png/)) { + if (!files[0].type.match(/image.*/)) { $app.$message({ message: "File isn't a png", type: 'error' @@ -22727,7 +22732,8 @@ speechSynthesis.getVoices(); this.changeAvatarImageDialogLoading = true; var r = new FileReader(); r.onload = async function (file) { - var base64File = btoa(r.result); + var base64File = await $app.resizeImageToFitLimits(btoa(r.result)); + // 10MB var fileMd5 = await $app.genMd5(base64File); var fileSizeInBytes = parseInt(file.total, 10); var base64SignatureFile = await $app.genSig(base64File); @@ -23041,8 +23047,8 @@ speechSynthesis.getVoices(); clearFile(); return; } - if (files[0].size >= 10000000) { - // 10MB + if (files[0].size >= 100000000) { + // 100MB $app.$message({ message: 'File size too large', type: 'error' @@ -23050,7 +23056,7 @@ speechSynthesis.getVoices(); clearFile(); return; } - if (!files[0].type.match(/image.png/)) { + if (!files[0].type.match(/image.*/)) { $app.$message({ message: "File isn't a png", type: 'error' @@ -23062,7 +23068,8 @@ speechSynthesis.getVoices(); this.changeWorldImageDialogLoading = true; var r = new FileReader(); r.onload = async function (file) { - var base64File = btoa(r.result); + var base64File = await $app.resizeImageToFitLimits(btoa(r.result)); + // 10MB var fileMd5 = await $app.genMd5(base64File); var fileSizeInBytes = parseInt(file.total, 10); var base64SignatureFile = await $app.genSig(base64File); @@ -25073,8 +25080,8 @@ speechSynthesis.getVoices(); if (!files.length) { return; } - if (files[0].size >= 10000000) { - // 10MB + if (files[0].size >= 100000000) { + // 100MB $app.$message({ message: 'File size too large', type: 'error' @@ -25184,8 +25191,8 @@ speechSynthesis.getVoices(); if (!files.length) { return; } - if (files[0].size >= 10000000) { - // 10MB + if (files[0].size >= 100000000) { + // 100MB $app.$message({ message: 'File size too large', type: 'error' diff --git a/html/src/index.pug b/html/src/index.pug index ae38d12e..4a599af4 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -2428,7 +2428,7 @@ html //- dialog: Change avatar image el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeAvatarImageDialog" :visible.sync="changeAvatarImageDialogVisible" :title="$t('dialog.change_content_image.avatar')" width="850px") div(v-if="changeAvatarImageDialogVisible" v-loading="changeAvatarImageDialogLoading") - input(type="file" accept="image/*" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none") + input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none") span {{ $t('dialog.change_content_image.description') }} br el-button-group(style="padding-bottom:10px;padding-top:10px") @@ -2443,7 +2443,7 @@ html //- dialog: Change world image el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeWorldImageDialog" :visible.sync="changeWorldImageDialogVisible" :title="$t('dialog.change_content_image.world')" width="850px") div(v-if="changeWorldImageDialogVisible" v-loading="changeWorldImageDialogLoading") - input(type="file" accept="image/*" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none") + input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none") span {{ $t('dialog.change_content_image.description') }} br el-button-group(style="padding-bottom:10px;padding-top:10px") @@ -2472,7 +2472,7 @@ html el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogGalleryLoading") span(slot="label") {{ $t('dialog.gallery_icons.gallery') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64 - input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") + input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") el-button-group el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }} el-button(type="default" size="small" @click="displayGalleryUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }} @@ -2487,7 +2487,7 @@ html el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading") span(slot="label") {{ $t('dialog.gallery_icons.icons') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ VRCPlusIconsTable.length }}/64 - input(type="file" accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") + input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") el-button-group el-button(type="default" size="small" @click="refreshVRCPlusIconsTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }} el-button(type="default" size="small" @click="displayVRCPlusIconUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }} @@ -2502,7 +2502,7 @@ html el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading") span(slot="label") {{ $t('dialog.gallery_icons.emojis') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ emojiTable.length }}/9 - input(type="file" accept="image/*" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none") + input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none") el-button-group(style="margin-right:10px") el-button(type="default" size="small" @click="refreshEmojiTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }} el-button(type="default" size="small" @click="displayEmojiUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }} @@ -2977,7 +2977,7 @@ html span(slot="label") {{ $t('dialog.gallery_select.gallery') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64 br - input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") + input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") el-button-group el-button(type="default" size="small" @click="selectImageGallerySelect('', '')" icon="el-icon-close") {{ $t('dialog.gallery_select.none') }} el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_select.refresh') }}