User Image Upload in React without quality Loss - javascript

I'm working at a company where a File Uploader was already implemented, but for some reason we are saving the photo and its quality is greatly reduced. Basically, I want to know what the easiest way is to implement a component where a user can upload a profile photo or logo, and we can save it without losing quality. I also want to be able to resize it to various dimensions without losing quality. I don't necessarily need a cropper either, I want the user to be able to upload the photo and we display the full image, just resized without quality loss. Techstack is using JS and React.
class FileUpload extends React.Component {
constructor(props) {
super(props);
this.fileRef = createRef();
this.imgCanvas = createRef();
this.previousMoveX = 0;
this.previousMoveY = 0;
this.state = {
showUploadModal: false || props.showUploadModal,
img: '',
imgControl: false,
imgControlZoom: 0,
imgControlMove: false,
imgControlX: 0,
imgControlY: 0,
};
this.onImgRead = this.onImgRead.bind(this);
this.onChangeImage = this.onChangeImage.bind(this);
this.onChangeZoom = this.onChangeZoom.bind(this);
this.onStartMove = this.onStartMove.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
}
componentDidUpdate() {
const { img, imgControlZoom, imgControlX, imgControlY } = this.state;
if (img === '') return;
let canvas = this.imgCanvas.current;
if (canvas === null) return;
let ctx = canvas.getContext('2d');
let imgNode = new Image();
imgNode.onload = () => {
const s = parseInt(
Math.min(imgNode.width, imgNode.height) / ((imgControlZoom + 100) / 100)
);
const k = canvas.width / s;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
imgNode,
0,
0,
imgNode.width,
imgNode.height,
imgControlX + (s - imgNode.width) * 0.5 * k,
imgControlY + (s - imgNode.height) * 0.5 * k,
imgNode.width * k,
imgNode.height * k
);
};
imgNode.src = img;
}
onChangeImage(e) {
const input = e.target;
const { files } = e.target;
if (files === 0) return;
const file = files[0];
if (file.size > FILE_SIZE) {
toast.error(FILE_SIZE_VALIDATION);
return;
}
const fileReader = new FileReader();
fileReader.onload = ev => {
input.value = '';
this.onImgRead(ev.target.result);
};
fileReader.readAsDataURL(file, 'UTF-8');
}

This has nothing to do with React, and is not usually implemented on the client with JS, unless you are using NodeJS on the server. This feature is usually implemented on the server. Such as http_image_filter_module module of nginx.
For lossy image formats (such as jpeg), any change will again lose the quality of the image, although this loss is usually very small. If you really need to have a very high quality of the image, you need to convert them to lossless image formats (such as png) on the server first.

Related

Blurred pdf with pdf.js library

I'm using pdf.js library to render my pdf file on canvas.
First I was searching a solution for rendering pdf with the size of canvas parent element.
I've found it and it works fine.
Then I solve the problem of rendering ALL pages at once.
Finally, my code now looks this way:
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
class PdfLoader {
currPage = 1;
numPages = -1;
doc = null;
constructor($container) {
this.$container = $container;
}
load(path) {
// reset when using more than once
this.currPage = 1;
this.promise = new Promise(resolve => this.promiseResolve = resolve);
this.$container.innerHTML = "";
pdfjsLib.getDocument(path).promise.then(pdf => {
this.doc = pdf;
this.numPages = pdf.numPages;
pdf.getPage(1).then(this._handlePage.bind(this));
});
return this;
}
_handlePage(page) {
let viewport = page.getViewport({scale: 1});
const scale = this.$container.clientWidth/viewport.width;
// const outputScale = window.devicePixelRatio || 1;
const outputScale = 1;
viewport = page.getViewport({scale});
const cnv = document.createElement("canvas");
const ctx = cnv.getContext("2d");
const width = Math.floor(viewport.width);
const height = Math.floor(viewport.height);
cnv.width = Math.floor(width * outputScale);
cnv.height = Math.floor(height * outputScale);
cnv.style.width = `${width}px`;
cnv.style.height = `${height}px`;
const transform = (outputScale !== 1) ? [outputScale, 0, 0, outputScale, 0, 0] : null;
page.render({
canvasContext: ctx,
transform: transform,
viewport: viewport,
});
this.$container.appendChild(cnv);
this.currPage++;
if (this.doc !== null && this.currPage <= this.numPages) {
this.doc.getPage(this.currPage).then(this._handlePage.bind(this));
} else {
this.promiseResolve();
}
}
}
const $pages = document.getElementById("pages");
const pdfLoader = new PdfLoader($pages);
pdfLoader.load("extendedReport.pdf").promise
.then(initReport);
let arrow = null;
function initReport() {
// my other stuff
}
And now my problem is that when viewing rendered pdf it looks like its quality is very low and text is blurred so the document is unreadable on mobile devices. I tried to change passed scale like they say on the internet, but that's not it. Could you help me, plz? What am I missing?

How to download pdf in nuxt project?

<div style="height: 0;position: fixed; left: 50%; top: 0; transform: translateX(-50%);
z-index: -1;overflow: hidden">
<ticket-pdf v-if="pdfData" :pdfData="pdfData" :downloadBtn="downloadBtn" #download="downloadBtn = $event" /> </div>
In my nuxt project, i need to download pdf file in my main componet .When user clicked download button it will be downloaded directlty without previewing pdf component. So i hide my ticket-pdf component inside my main component . This way works in localhost but when i published my project to test area it does not work. It download only 1 empty page. and i face with this mistake.
Failed to load resource: the server responded with a status of 500 ()
If i open my pdf file in a other page and i can preview and download it also. But i need to doownload it without routing anywhere and directly in my main component
printPDF() {
const html2canvasOptions = {
allowTaint: true,
removeContainer: true,
imageTimeout: 15000,
logging: true,
useCORS: true,
scrollX: 0,
scrollY: 0,
scale: 2,
};
// Initialize the PDF.
let pdf = new jsPDF('p', 'in', [8.5, 11], false);
let totalPage = 0
//i have many pages and countof pages changes . It depends. so firstly i calculate page count and create pdf.
let pageCount = document.querySelectorAll('[id^=contentPrint]').length
for (let i = 0; i < pageCount; i++) {
const contentPrint = document.getElementById('contentPrint-' + i);
html2canvas(contentPrint, html2canvasOptions)
.then((canvas) => {
const image = {type: 'jpeg', quality: 1};
const margin = [0.5, 0.5];
let imgWidth = 8.5;
let pageHeight = 11;
let innerPageWidth = imgWidth - margin[0] * 2;
let innerPageHeight = pageHeight - margin[1] * 2;
// Calculate the number of pages.
let pxFullHeight = canvas.height;
let pxPageHeight = Math.floor(canvas.width * (pageHeight / imgWidth));
let nPages = Math.ceil(pxFullHeight / pxPageHeight);
totalPage += nPages
// Define pageHeight separately so it can be trimmed on the final page.
pageHeight = innerPageHeight;
// Create a one-page canvas to split up the full image.
let pageCanvas = document.createElement('canvas');
let pageCtx = pageCanvas.getContext('2d');
pageCanvas.width = canvas.width;
pageCanvas.height = pxPageHeight;
for (let page = 0; page <= nPages; page++) {
// Trim the final page to reduce file size.
if (page === nPages - 1 && pxFullHeight % pxPageHeight !== 0) {
pageCanvas.height = pxFullHeight % pxPageHeight;
pageHeight = (pageCanvas.height * innerPageWidth) / pageCanvas.width;
}
// Display the page.
let w = pageCanvas.width;
let h = pageCanvas.height;
pageCtx.fillStyle = 'white';
pageCtx.fillRect(0, 0, w, h);
pageCtx.drawImage(canvas, 0, page * pxPageHeight, w, h, 0, 0, w, h);
// Add the page to the PDF.
if (page > 0) pdf.addPage();
let imgData = pageCanvas.toDataURL('image/' + image.type, image.quality);
pdf.addImage(imgData, image.type, margin[1], margin[0], innerPageWidth, pageHeight);
}
});
}
setTimeout(() => {
// pdf.deletePage(totalPage + 1)
pdf.save("e-ticket" + '.pdf')
}, 3000)
setTimeout(() => {
this.$emit("download", this.download);
}, 4000)
},
This is how i create pdf area. When user clicked download button i create pdf and download it thanks to my props.
printPDF() {
window.scrollTo(0, 0);
// Initialize the PDF.
let pdf = new jsPDF('p', 'in', [8.5, 11], false);
let totalPage = 0
let counter = 0
let myArray = []
let pageCount = document.querySelectorAll('[id^=contentPrint]').length
for (let i = 0; i < pageCount; i++) {
myArray.push('contentPrint-' + i)
}
for (let i = 0; i < pageCount; i++) {
const contentPrint = document.getElementById('contentPrint-' + i);
html2canvas(contentPrint, {
scale: 2, useCORS: true, allowTaint: true, scrollY: 0
}).then((canvas) => {
.................
........
.....
counter = counter + 1;
callBack();
})
}
const callBack = () => {
if (myArray.length === counter) {
pdf.save("filename" + '.pdf')
this.$emit("download", this.download);
}
}
},
the problem was about my settimeout function . ın there ı would call pdf.save before pdf created and sure every browser works in different speed. so if we use callback function after all process done , then we can obtain our pdf rpint
You could maybe try to simply put
<a href="/your_file.csv" download>
by putting your_file.csv into the static directory.
Not sure if it can be enough in your case, sorry if it's not.

Firefox extension not able to select all images may be due to Canvas fuction getImageData()

In the below code I have imported my machine learning model through function model_startup(). model_startup() is working fine. But after that, I am getting into function blockk(), through this function I am trying to select all images on website by document.getElementsByTagName('img') and do prediction through that image with the help of an imported model. But my problem is that my second function is not selecting all images on website and selecting only some number of starting images and after prediction on that some number of images it get stopped.
I am new to javascript and trying to implement it as a firefox extension. Please help me in this problem
console.log('STARTING UP')
const MODEL_PATH = 'mdl/model.json'
const IMAGE_SIZE = 64;
let model;
async function model_startup()
{
console.log('Launching TF.js!');
tf.ENV.set('WEBGL_PACK',false);
await tf.ready();
console.log('TensorflowJS backend is: '+tf.getBackend());
let url = browser.runtime.getURL(MODEL_PATH);
console.log('Loading model... '+url);
try
{
model = await tf.loadLayersModel(url);
}
catch(e)
{
console.log('Failed to load model! '+e);
}
console.log('Model: ' + model);
console.log('Warming up...');
let dummy_data = tf.zeros([1, IMAGE_SIZE, IMAGE_SIZE, 3]);
// dummy_data.print();
let warmup_result = model.predict(dummy_data);
console.log("finding type : $$$$$$$$ " + typeof warmup_result)
warmup_result.print();
warmup_result.dispose();
console.log('Ready to go!');
// blockk();
};
model_startup().then(blockk);
function blockk() {
const x = document.getElementsByTagName('img');
console.log("wn ############################## " + x.length);
let inferenceCanvas = document.createElement('canvas');
inferenceCanvas.width = 64;
inferenceCanvas.height = 64;
let inferenceCtx = inferenceCanvas.getContext('2d', { alpha: false});
inferenceCtx.imageSmoothingEnabled = true;
for(var i = 0; i < x.length; i++)
{
console.log('pixel kmvsl lsvn');
let img = x[i];
inferenceCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 64, 64);
const rightSizeImageData = inferenceCtx.getImageData(0, 0, 64, 64); //write promise. It may work.
const rightSizeImageDataTF = tf.browser.fromPixels(rightSizeImageData);
const floatImg = rightSizeImageDataTF.toFloat();
console.log('predicting...');
let scaled = floatImg.div(tf.scalar(255));
let batched = tf.stack([scaled]);
let result = model.predict(batched);
const val = result.dataSync()[0];
console.log(val);
if (val > 0.4)
{
console.log("blocking" + i);
let h = img.height
let w = img.width
pth = img.src
x[i].setAttribute("src", "https://indianonlineseller.com/wp-content/uploads/2017/05/blocked-listing.png");
// x[i].setAttribute("onclick", ` this.src = '${pth}' `);
x[i].setAttribute("data-src", "https://indianonlineseller.com/wp-content/uploads/2017/05/blocked-listing.png");
x[i].height = h;
x[i].width = w;
console.log('blocked');
}
console.log("co ########################### " + x.length)
}
};
The line in my code which is causing problem is
const rightSizeImageData = inferenceCtx.getImageData(0, 0, 64, 64);
If I remove this code and other code related to this line and I run it then my code is able to select all the images on webpage. Can you tell me why this may be happening?

How to detect, that drawing a canvas object is finished?

I have following JS code (found here, on stackoverflow, and a little-bit modded), which resize image on client side using canvas.
function FileListItem(a) {
// Necesary to proper-work of CatchFile function (especially image-resizing).
// Code by Jimmy Wärting (https://github.com/jimmywarting)
a = [].slice.call(Array.isArray(a) ? a : arguments)
for (var c, b = c = a.length, d = !0; b-- && d;) d = a[b] instanceof File
if (!d) throw new TypeError('expected argument to FileList is File or array of File objects')
for (b = (new ClipboardEvent('')).clipboardData || new DataTransfer; c--;) b.items.add(a[c])
return b.files
}
function CatchFile(obj) {
// Based on ResizeImage function.
// Original code by Jimmy Wärting (https://github.com/jimmywarting)
var file = obj.files[0];
// Check that file is image (regex)
var imageReg = /[\/.](gif|jpg|jpeg|tiff|png|bmp)$/i;
if (!file) return
var uploadButtonsDiv = document.getElementById('upload_buttons_area');
// Check, that it is first uploaded file, or not
// If first, draw a div for showing status
var uploadStatusDiv = document.getElementById('upload_status_area');
if (!uploadStatusDiv) {
var uploadStatusDiv = document.createElement('div');
uploadStatusDiv.setAttribute('class', 'upload-status-area');
uploadStatusDiv.setAttribute('id', 'upload_status_area');
uploadButtonsDiv.parentNode.insertBefore(uploadStatusDiv, uploadButtonsDiv.nextSibling);
// Draw sub-div for each input field
var i;
for (i = 0; i < 3; i++) {
var uploadStatus = document.createElement('div');
uploadStatus.setAttribute('class', 'upload-status');
uploadStatus.setAttribute('id', ('upload_status_id_commentfile_set-' + i + '-file'));
uploadStatusDiv.append(uploadStatus);
}
}
var canvasDiv = document.getElementById('canvas-area');
var currField = document.getElementById(obj.id);
var currFieldLabel = document.getElementById(('label_' + obj.id));
// Main image-converting procedure
if (imageReg.test(file.name)) {
file.image().then(img => {
const canvas = document.createElement('canvas')
canvas.setAttribute('id', ('canvas_' + obj.id));
const ctx = canvas.getContext('2d')
const maxWidth = 1600
const maxHeight = 1200
// Calculate new size
const ratio = Math.min(maxWidth / img.width, maxHeight / img.height)
const width = img.width * ratio + .5|0
const height = img.height * ratio + .5|0
// Resize the canvas to the new dimensions
canvas.width = width
canvas.height = height
// Drawing canvas-object is necessary to proper-work
// on mobile browsers.
// In this case, canvas is inserted to hidden div (display: none)
ctx.drawImage(img, 0, 0, width, height)
canvasDiv.appendChild(canvas)
// Get the binary (aka blob)
canvas.toBlob(blob => {
const resizedFile = new File([blob], file.name, file)
const fileList = new FileListItem(resizedFile)
// Temporary remove event listener since
// assigning a new filelist to the input
// will trigger a new change event...
obj.onchange = null
obj.files = fileList
obj.onchange = CatchFile
}, 'image/jpeg', 0.70)
}
)
// If file is image, during conversion show status
function ShowConvertConfirmation() {
if (document.getElementById('canvas_' + obj.id)) {
document.getElementById(('upload_status_' + obj.id)).innerHTML =
'<font color="#4CAF50">Konwertowanie pliku ' + file.name + ' zakończone!</font>';
return true;
}
else {
document.getElementById(('upload_status_' + obj.id)).innerHTML =
'<font color="#4CAF50">Konwertowanie pliku ' + file.name + ' zakończone!</font>';
return false;
}
}
// Loop ShowConvertConfirmation function untill return true (file is converted)
var convertConfirmationLoop = setInterval(function() {
var isConfirmed = ShowConvertConfirmation();
if (!isConfirmed) {
ShowConvertConfirmation();
}
else {
// Break loop
clearInterval(convertConfirmationLoop);
}
}, 2000); // Check every 2000ms
}
// If file is not an image, show status with filename
else {
document.getElementById(('upload_status_' + obj.id)).innerHTML =
'<font color="#4CAF50">Dodano plik ' + file.name + '</font>';
//uploadStatusDiv.append(uploadStatus);
}
}
Canvas is drawn in hidden div:
<div id="canvas-area" style="overflow: hidden; height: 0;"></div>
I am only detect, that div canvas-area is presented and basing on this, JS append another div with status.
Unfortunatelly on some mobile devices (mid-range smartphones), message will be showed before finish of drawing (it is wrong). Due to this, some uploaded images are corrupted or stay in original size.
How to prevent this?
Everything that should happen after the image has loaded, should be executed within the then callback, or called from within it.
It is important to realise that the code that is not within that callback will execute immediately, well before the drawing has completed.

Black images from canvas.toDataURL()

I have created a web application where users can upload images from their filesystem but in few cases I only get black image data on the server side. I have never got this issue in my development environment on lan even with the original images causing the error. This failure occurs overall only sporadic but especially one user is affected. All users should work with the latest firefox. I have read most of the question about this topic and I dont think this is related to a security issue, the jpg/png setting or preserveDrawingBuffer. Because I have never got this problem on lan with a fast PC and the fact that often the last selected images are affected it seems to be a synchronization problem.
To avoid this I work with $.Deferred and Callbacks but may be I missed something. First I load and convert the selected images directly into a list of canvas. The upload is disabled until this is finished. During the upload I just loop through the canvas list and read the data (canvas.toDataURL()) with a $.Deferred and transfer it to the server via ajax.
Get the images to the page:
var file = document.getElementById('fileSelect');
if (file != undefined) {
file.onchange = function (e) {
btnUpload.SetEnabled(false);
loadImgs(e, function () {
btnUpload.SetEnabled(true);
});
};
}
function loadImgs(e, callback) {
for (var t = 0; t < e.target.files.length; t++) {
if ($('.CanvasClass').length <= 20) {
var myTmpImg = new Image();
var myName = e.target.files[t].name;
myTmpImg.src = URL.createObjectURL(e.target.files[t]);
myTmpImg.onload = function () {
var GrenzeX = 1024;
var GrenzeY = 768;
var myFactor = 1;
if (myTmpImg.naturalWidth > GrenzeX || myTmpImg.naturalHeight > GrenzeY) {
if ((GrenzeX / myTmpImg.naturalWidth) < (GrenzeY / myTmpImg.naturalHeight)) {
myFactor = GrenzeX / myTmpImg.naturalWidth;
} else {
myFactor = GrenzeY / myTmpImg.naturalHeight;
}
}
var myCanvas = document.createElement('canvas');
myCanvas.className = "CanvasClass";
myCanvas.height = this.naturalHeight * myFactor;
myCanvas.width = this.naturalWidth * myFactor;
var ctx = myCanvas.getContext("2d");
ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
$("#myImages").append(myCanvas);
myMetaData.push(new newImgData('', '', myName));
}
}
}
if (callback && typeof callback == "function") {
callback();
}
}
Read and transfer the Data:
$('.CanvasClass').each(function () {
var fd = new FormData();
var base64Data = getBase64Image(this, false);
base64Data.then(function (result) {
//Create Ajax Request
});
});
function getBase64Image(myCanvas, modus) {
var deferred = $.Deferred();
var dataURL = myCanvas.toDataURL("image/jpeg", 0.9);
if (modus === true) {
deferred.resolve(dataURL);
} else {
deferred.resolve(dataURL.replace(/^data:image\/(png|jpeg);base64,/, ""));
}
return deferred.promise();
}
I can also exclude issues during the data transfer because I logged the size of the images on clientside before uploading and compared this to the data at serverside and it was the same.

Categories

Resources