I am trying to trouble shoot/fix my nodejs upload image api:
My service is being stuck at somewhere.
My service is too simple, just uploading the image by resizing through sharp api in a directory and return the full path of that file.
When I select some image at first time then everything works fine and image upload successfully with a response
but When I try to crop that image after clicking on the image and try to save it (at second time) then nodejs service return following response.
I don't why it is being happened, I tried to debug the service code and It didn't stop at anywhere. Flow has been passed/executed successfully, I didn't catch any exception/error in code.
What can be the issue in it because it still displaying
> Blockquote failed to load response data. no resource with given identifier found
Problem area is in the code of onSaveImage when a server side call is being served/requested. I am using the plugin for image cropping is react-easy-crop. Browser is getting refresh/reload at this line of code
const jsonRes = await responseMain.json();
I am sharing my nodejs service code as well as reactjs code. please look into it. Thank you.
-----------------package.json of nodejs
{
"name": "",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-validator": "^6.12.0",
"handlebars": "^4.7.7",
"jsonwebtoken": "^8.5.1",
"mysql": "^2.18.1",
"nodemailer": "^6.6.1",
"nodemon": "^2.0.12",
"sharp": "^0.29.3"
},
"devDependencies": {},
"scripts": {
"start": "nodemon --inspect index.js",
"debug": "node --nolazy --inspect-brk=9229 index.js"
},
"license": "ISC"
}
------ index.js---------------------------NodeJs
const express = require("express");
const app = express();
const cors = require("cors");
var fs = require("fs");
const fsp = fs.promises;
var path = require("path");
const sharp = require("sharp");
var $pathContentBuilder = path.join(__dirname, "../", "/public/roughdata/uploads/");
var $urlpathContentBuilder = "/roughdata/uploads/"; // URL path
app.use(express.json({ limit: "20mb" }));
app.use(cors());
app.use(
express.urlencoded({
extended: true,
})
);
function processImage(image, metadata, filename, isResizeAllow) {
return new Promise((resolve, reject) => {
if (isResizeAllow && isResizeAllow === true) {
// 0.8 MB
return image
.resize({
width: Math.round(metadata.width / 2),
height: Math.round(metadata.height / 2),
fit: "cover",
})
.toBuffer((err, buf) => {
if (err) {
console.log("Error occured ", err);
return reject(err);
} else {
return resolve(buf.toString("base64"));
}
});
} else {
return image.toBuffer((err, buf) => {
if (err) {
console.log("Error occured ", err);
return reject(err);
} else {
return resolve(buf.toString("base64"));
}
});
}
});
}
app.post("/uploaddetail", async (req, res, next) => {
const base64Data = req.body.image;
const filename = req.body.filename;
let imageResizeResult = "";
try {
var inputbuf = Buffer.from(base64Data, "base64"); // Ta-da
const image = await sharp(inputbuf);
let metadata = await image.metadata();
let convertedbase64Data = "";
convertedbase64Data = await processImage(image, metadata, filename, false);
await fsp.writeFile($pathContentBuilder + filename, convertedbase64Data, "base64");
let resultResponse = JSON.stringify({
success: true,
fullurl: `${$urlpathContentBuilder}${filename}`,
url: `${filename}`,
imagename: `${filename}`,
serverpath: `${$urlpathContentBuilder}`,
});
//res.type("json");
res.status(200).json(resultResponse);
//res.end();
//next();
} catch (e) {
console.log(e);
const error = new HttpError(e, 404);
return next(error);
}
});
and following is my reactjs code.
import React, { useState, useEffect, useCallback } from "react";
import { withRouter, useParams, useHistory } from "react-router-dom";
import { Card, Button } from "react-bootstrap";
import Page from "./Page";
import TitleBar from "./TitleBar";
import SingleGalleryRow from "./SingleGalleryRow";
import CenteredPopup from "../common/CenteredPopup";
import { MdDelete, MdOutlineCrop, MdOutlineCircle } from "react-icons/md";
import Cropper from "react-easy-crop";
import getCroppedImg from "../shared/util/cropImage";
import b64toBlob from "../shared/util/blobToImage";
function EditImage({ breadcrumb, pageName, setLoader, showToast }) {
const [list, setList] = useState([]);
const history = useHistory();
const [umodalShow, setUModalShow] = useState(false);
const [row, setRow] = useState({
ImageName: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
Name: "",
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
const [isSubmitClicked, setIsSubmitClicked] = useState(false);
const [IsDisabled, setIsDisabled] = useState(false);
const [nameError, setNameError] = useState("");
const [validated, setValidated] = useState(0);
const [ImageName, setImageName] = useState("");
const [base64SelectedImage, setBase64SelectedImage] = useState("");
const { id } = useParams();
let content = "";
const [detail, setDetail] = useState({
Content: "",
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
Name: "",
SiteId: 1,
});
useEffect(() => {
let abortController = new AbortController();
let mounted = true;
const fetchData = async () => {
try {
setLoader(true);
let apiUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/getbyId/" + id;
const response = await fetch(apiUrl, {
signal: abortController.signal,
});
const json = await response.json();
const resultPages = json ? json.list : "";
if (resultPages && resultPages.length > 0) {
//debugger;
let firstRow = resultPages[0];
let isEnabled = false;
if (firstRow.IsVisible === 1 || firstRow.IsVisible === true) {
isEnabled = true;
}
let newDetailPage = {
Id: firstRow.Id,
Name: firstRow.Name,
Content: firstRow.Content
};
if (mounted) {
setDetail(newDetailPage);
}
let itemRows = [];
for (let item of resultPages) {
let row = {
Id: item.DetailId,
ImageSliderId: item.ImageSliderId,
ImageName: item.ImageName,
ImageFullUrl: item.ImageFullUrl,
};
if (item.ImageFullUrl) {
itemRows.push(row);
}
}
if (itemRows && itemRows.length > 0) {
if (mounted) {
setList(itemRows);
}
}
}
setLoader(false);
//abortController = null;
} catch (err) {
setLoader(false);
if (err.name === "AbortError") {
// Handling error thrown by aborting request
}
}
};
fetchData();
// Side-effect logic...
return () => {
// Side-effect cleanup
mounted = false;
abortController.abort();
console.log("unmounting...");
};
}, []);
const [imageCropWidth, setImageCropWidth] = useState(100);
const [imageCropHeight, setImageCropHeight] = useState(100);
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [aspect, setAspect] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
const [croppedImage, setCroppedImage] = useState(null);
const [cropSize, setCropSize] = useState({ width: 100, height: 100 });
const [Image, setImage] = useState("");
const [rotation, setRotation] = useState(0);
const [cropShape, setCropShape] = useState("rect");
const onSaveImage = useCallback(
async (e) => {
e.preventDefault();
if (validate() < 0) {
return -1;
}
try {
let filename = "";
let base64Image = "";
let blobUrl = "";
debugger;
const cropImage = await getCroppedImg(Image, croppedAreaPixels, rotation);
setCroppedImage(cropImage);
setImage(cropImage);
base64Image = cropImage ? cropImage.replace(/^data:image\/(png|jpeg);base64,/, "") : "";
const contentType = "image/jpeg";
if (cropImage) {
debugger;
setRow((prevState) => {
return {
...prevState,
["ImageFullUrl"]: cropImage,
["ImageName"]: ImageName ? ImageName : "",
};
});
filename = ImageName ? ImageName : "";
const settings = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
filename: filename,
image: base64Image,
}),
};
setLoader(true);
let submitUrl = process.env.REACT_APP_SERVER_DOMAIN + `/uploaddetail`;
const responseMain = await fetch(submitUrl, settings);
const jsonRes = await responseMain.json();
if (jsonRes) {
let convertedJson = jsonRes;
if (convertedJson) {
setLoader(false);
const uploadedImageUrl = convertedJson.fullurl; // get saved image url
const uploadedImageName = convertedJson.imagename;
const uploadedServerPath = convertedJson.serverpath;
const uploadurl = convertedJson.url;
setImageName(convertedJson.imagename);
setRow((prevState) => {
return {
...prevState,
["ImageName"]: uploadedImageName,
["ImageServerPath"]: uploadedServerPath,
["ImageFullUrl"]: uploadedImageUrl,
["ImageUrl"]: uploadurl,
};
});
if (uploadedImageUrl) {
setIsDisabled(false);
}
setUModalShow(false);
setDetail((prevState) => {
return {
...prevState,
["Content"]: content,
};
});
}
}
}
} catch (err) {
console.log(err);
showToast("Error!", "Image couldn't be saved");
} finally {
}
},
[croppedAreaPixels, rotation]
);
const showImageForCrop = useCallback(
async (e) => {
debugger;
let localSrc = e.ImageFullUrl; //e.target.src;
setImage(localSrc);
setUModalShow(true);
setImageName(e.ImageName);
setCropSize({ width: imageCropWidth ? parseInt(imageCropWidth) : 0, height: imageCropHeight ? parseInt(imageCropHeight) : 0 });
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id === e.Id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
},
[row]
);
const singlefileSelectedHandler = async (e) => {
//debugger;
e.preventDefault();
setIsDisabled(true);
content = "";
if (!e.target.files || e.target.files.length <= 0) {
return -1;
}
if (detail && detail.Content) {
content = detail.Content;
}
//let imageList = "";
const selectedImage = e.target.files[0];
let imageSizeInMBs = selectedImage.size ? selectedImage.size / 1024 / 1024 : 0;
if (selectedImage && imageSizeInMBs > 8) {
setIsDisabled(false);
setRow((prevState) => {
return {
...prevState,
["ImageName"]: "",
["ImageServerPath"]: "",
["ImageFullUrl"]: "",
["ImageUrl"]: "",
};
});
showToast("Information!", "Image size can't be greater than 8MB.");
return -1;
}
const filename =
selectedImage && selectedImage.name
? new Date().valueOf() +
"_" +
selectedImage.name.replace(/\\/g, "").replace(/ /g, "").replace(/'/g, "").replace(/"/g, "").replace(/`/g, "")
: "";
setUModalShow(false);
setCropSize({ width: imageCropWidth ? parseInt(imageCropWidth) : 0, height: imageCropHeight ? parseInt(imageCropHeight) : 0 });
setImageName(filename);
setUModalShow(true);
let convertedImageFile = await convertImage(selectedImage);
setBase64SelectedImage(convertedImageFile);
};
async function convertImage(file) {
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
e.preventDefault();
let base64 = e.target.result;
base64 = base64.replace(/^data:image\/(png|jpeg);base64,/, "");
setImage(reader.result);
resolve(base64);
//resolve(reader.result);
};
reader.onerror = function (error) {
console.log("Error: ", error);
reject(error);
};
});
}
const onAddSide = (e) => {
//debugger;
let newImageList = [...list];
if (row.Id && row.Id > 0) {
if (newImageList && newImageList.length > 0) {
const selectedIndex = newImageList.findIndex((item) => item.Id === row.Id);
if (selectedIndex > -1) {
newImageList[selectedIndex] = row;
}
}
} else {
row.Id = Math.max.apply(
Math,
newImageList.map(function (o) {
return o.Id + 1;
})
);
if (!row.Id || (row.Id && row.Id === 0) || row.Id === -Infinity) {
row.Id = 1;
}
newImageList.push(row);
}
setList(newImageList);
};
const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
//debugger;
setCroppedAreaPixels(croppedAreaPixels);
}, []);
const onClose = useCallback(() => {
setCroppedImage(null);
}, []);
const onCropperZoomChange = (e) => {
if (e && e.currentTarget && e.currentTarget.value) {
let lZoomValue = e.currentTarget.value;
if (lZoomValue && lZoomValue < 0.4) {
//setZoom(0.28);
setZoom(0.4);
} else if (lZoomValue && lZoomValue > 1) {
setZoom(1);
} else {
setZoom(lZoomValue);
}
}
};
return (
<Page breadcrumb={breadcrumb} pageName={pageName} fluid>
<form noValidate validated={validated} onSubmit={HandleSaveMenu} className="user-form">
<Card>
<Card.Header>
<TitleBar title="Edit Gallery" />
</Card.Header>
<Card.Body>
<div className="form-row">
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Width
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropWidth}
onChange={(e) => setImageCropWidth(e.target.value)}
/>
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Height
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropHeight}
onChange={(e) => setImageCropHeight(e.target.value)}
/>
</div>
</div>
<SingleGalleryRow
data={row}
setRow={setRow}
onUpload={singlefileSelectedHandler}
onAddSide={onAddSide}
IsDisabled={IsDisabled}
/>
<ul className="slide-list">
{list &&
list.map(function (item, i) {
//debugger;
return (
<li key={i}>
<div className="slide-row">
<div className="img-holder">
<img
className="thumbnail"
src={item.ImageFullUrl}
alt={item.ImageName}
onClick={() => showImageForCrop(item)}
/>
</div>
</div>
</li>
);
})}
</ul>
</Card.Body>
<Card.Footer>
<div className="btn-holder text-right">
<Button type="submit" variant="primary" disabled={isSubmitClicked}>
Save
</Button>
</div>
</Card.Footer>
</Card>
</form>
<CenteredPopup
show={umodalShow}
onHide={() => setUModalShow(false)}
title="Crop Image"
content=""
closetext="Close"
savetext="Apply"
onSaveChanges={onSaveImage}
>
<div className="cropimage-container">
<div className="cropimage-tools">
<span className="cropimage-icons" onClick={(e) => setCropShape("rect")}>
<MdOutlineCrop />
<span className="cropimage-text">Crop</span>
</span>
<span className="cropimage-icons" onClick={(e) => setCropShape("round")}>
<MdOutlineCircle />
<span className="cropimage-text">Circle</span>
</span>
</div>
<div className="crop-container">
<Cropper
image={Image}
crop={crop}
cropSize={cropSize}
cropShape={cropShape}
zoom={zoom || 1}
showGrid={true}
zoomWithScroll={false}
objectFit="horizontal-cover"
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={onCropperZoomChange}
/>
</div>
</div>
<div className="form-row ic_rangeslider">
<div className="form-group col-md-12">
<label className="form-label" htmlFor="ZoomBtn">
{zoom || 1}
</label>
<span className="range-slider__wrap">
<input
id="zoomrangeid"
type="range"
className="slider range-slider range-slider--light"
value={zoom || 1}
min={0.4}
max={1}
step={0.1}
aria-labelledby="Zoom"
onChange={onCropperZoomChange}
/>
</span>
</div>
</div>
</CenteredPopup>
</Page>
);
}
export default withRouter(EditImage);


