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
Section titled “Intercepting the Upload”Server
Section titled “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
Section titled “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
Section titled “Examples”Save images to the server's disk
Section titled “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 diskfunction 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 licenseconst license = { clientId: '356a192b7913b04c54574d18c28d46e6395428ab', licenseKey: 'YOUR_LICENSE_KEY'};
// Create the Serverserver({ 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
Section titled “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
Section titled “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 licenseconst license = { clientId: '356a192b7913b04c54574d18c28d46e6395428ab', licenseKey: 'YOUR_LICENSE_KEY'}
// Define S3 configurationapi({ 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
Section titled “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 } }, }});