Spreadsheet Images
When a user uploads an image, it’s stored as base64 in the spreadsheet's JSON configuration by default, increasing server storage needs. So, saving images in an S3 bucket or similar external storage is recommended. This section brings more information about intercepting the image upload on the back end and in the front end.
Intercepting the Upload
Server
Use the beforeChange event to intercept image uploads on the server. This event lets you overwrite the image src, upload it to the appropriate service, and update its reference with a valid URL before saving it to the database.
Client
When images are stored externally, user requests should be redirected to the appropriate service with the necessary authentication token. Use the onbeforeloadimage
event to intercept the download, enabling the correct image download.
Examples
Save images to the server's disk
In this example, images are saved to a public folder on the server, making them accessible as static files.
const server = require('@jspreadsheet/server');
const adapter = require('@jspreadsheet/redis-adapter');
const fs = require('fs');
const path = require('path');
// Helper function to save the image to disk
function saveImageToDisk(base64Image, imageName) {
// Strip metadata from the base64 string
const base64Data = base64Image.replace(/^data:image\/\w+;base64,/, '');
// Define the directory where images will be saved
const imageDir = path.join(__dirname, 'public', 'images');
// Ensure the directory exists
if (! fs.existsSync(imageDir)) {
fs.mkdirSync(imageDir, { recursive: true });
}
// Full path for the image
const imagePath = path.join(imageDir, imageName);
// Write the image to the file system
fs.writeFileSync(imagePath, base64Data, { encoding: 'base64' });
// Return the URL of the image to be saved in the spreadsheet
return `/images/${imageName}`;
}
// Jspreadsheet license
const license = {
clientId: '356a192b7913b04c54574d18c28d46e6395428ab',
licenseKey: 'YOUR_LICENSE_KEY'
};
// Create the Server
server({
config: {
cors: {
origin: "*"
},
},
port: 3000,
error: function(e) {
console.error(e);
},
auth: async function(query) {
return true;
},
load: async function(guid) {
return await adapter.load(guid);
},
beforeChange: async function(guid, changes, query) {
// Check for image upload event
if (query && query.guid) {
// Check for the correct event that upload images
if (changes.method === 'setMedia' && Array.isArray(changes.args[0])) {
// Go through all images
for (const v of changes.args[0]) {
if (v.src && v.src.startsWith('data:')) {
// Generate a unique name for the image
const imageName = `image_${Date.now()}.png`;
// Save the image to disk
const imageUrl = saveImageToDisk(v.src, imageName);
// Replace the base64 src with the image URL
v.src = imageUrl;
}
}
}
}
return changes;
},
change: async function(guid, changes, query, onerror) {
return await adapter.change(guid, changes, query, onerror);
},
create: async function(guid, config, query) {
return await adapter.create(guid, config, query);
},
destroy: async function(guid, query) {
return await adapter.destroy(guid, query);
},
license: license
});
Using MongoDB and S3
This example shows how to intercept and save images to S3 when using the MongoDB adapter. If you're not using MongoDB, you can upload images directly to your S3 bucket and update the image URL accordingly.
Server-side
Intercept the setMedia
event to upload base64 images to S3.
const api = require('@jspreadsheet/api');
const server = require('@jspreadsheet/server');
const adapter = require('@jspreadsheet/mongodb-adapter');
// Jspreadsheet license
const license = {
clientId: '356a192b7913b04c54574d18c28d46e6395428ab',
licenseKey: 'YOUR_LICENSE_KEY'
}
// Define S3 configuration
api({
s3: {
key: "your-key",
secret: "your-secret",
bucket: "your-bucket",
region: "us-your-region",
url: "https://bucketname.s3.amazonaws.com/",
},
adapter: adapter
})
server({
config: {
cors: {
origin: "*"
},
},
port: 3000,
error: function(e) {
console.error(e)
},
auth: async function(query) {
return true;
},
load: async function(guid) {
return await adapter.load(guid);
},
beforeChange: async function(guid, changes, query) {
if (query && query.guid) {
if (changes.method === 'setMedia' && Array.isArray(changes.args[0])) {
for (const v of changes.args[0]) {
if (v.src && v.src.startsWith('data:')) {
// Upload to S3
let ret = await api.setImage(guid, v.src);
if (ret) {
v.src = ret; // Overwrite base64 with the S3 URL
}
}
}
}
}
return test;
},
change: async function(guid, changes, query, onerror) {
return await adapter.change(guid, changes, query, onerror);
},
create: async function(guid, config, query) {
return await adapter.create(guid, config, query);
},
destroy: async function(guid, query) {
return await adapter.destroy(guid, query);
},
license: license,
extensions: { api },
});
Client-side
Now, the image URLs will point to a private S3 route. The client intercepts the image load to call the API with the authentication bearer and retrieve the image from S3.
intrasheets({
client: {
url: 'http://localhost:3009',
token: token,
onbeforeloadimage: function (worksheet, img, options) {
if (options.src.includes('/api')) {
fetch('http://localhost:3009' + options.src, {
headers: {
'Authorization': 'Bearer ' + token
}
})
.then(response => response.blob())
.then(blob => {
img.src = URL.createObjectURL(blob); // Set the image source to the blob URL
});
return false; // Prevent default image load until the API call completes
}
},
}
});