I am working on a Chrome extension that scrapes the menu information of a restaurant on a food delivery platform (e.g. UberEats, FoodPanda) and stores it in an Excel file as well as downloading menu images, which are then compiled into a ZIP archive to be downloaded onto the disk. However, running FileSaver.js' saveAs function for the ZIP archive does not display the Save As dialog for the location to download the file. No error was thrown despite the Save As dialog not displaying on calling saveAs.
Manifest
{
"manifest_version": 3,
"name": "Menu scraper",
"description": "Scrapes menu information and downloads a zip file containing an Excel file on menu information and menu image files",
"version": "0.1",
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "content.js"
},
"content_scripts": [
{
"matches": [
"www.foodpanda.my"
],
"js": [
"xlsx.full.min.js",
"jszip.min.js",
"FileSaver.min.js",
"scraper.js",
"export.js"
]
}
],
"host_permissions": [
"www.foodpanda.my",
"image-hosting-site.com"
],
"permissions": [
"tabs",
"activeTab",
"scripting",
"contextMenus",
"downloads",
"storage"
]
}
content.js
importScripts('./xlsx.full.min.js')
importScripts("./jszip.min.js")
importScripts("./FileSaver.min.js")
importScripts("./scraper.js")
importScripts("./export.js")
chrome.contextMenus.create({
"id": "scrape_menu",
"title": "Save menu to Excel file",
"contexts": ["page"]
})
chrome.contextMenus.onClicked.addListener((clickData) => {
if (clickData.menuItemId == "scrape_menu") {
chrome.tabs.query({'active': true, 'windowId': chrome.windows.WINDOW_ID_CURRENT},
function(tabs){
var tab = tabs[0]
// Run scraper script then pass data to export script
chrome.scripting.executeScript({target: { tabId: tab.id }, func: scrape_menu,
}, function(selection) {
var vendor = selection[0].result
if (vendor) {
console.log(vendor)
export_vendor(vendor)
}
});
}
);
}
})
export.js
async function export_vendor(vendor) {
// Create workbook
var name = vendor['name']
var dishes = vendor['dishes']
console.log(vendor)
console.log("Creating menu for "+name)
var wb = XLSX.utils.book_new()
wb.Props = {
Title: name
}
// Insert menu contents
var ws = XLSX.utils.aoa_to_sheet(dishes, { origin: 'A2', skipHeader: true });
// Add header
let heading = [['No.', 'Name', 'Category', 'Price', 'Description', 'Image URL']];
XLSX.utils.sheet_add_aoa(ws, heading);
XLSX.utils.book_append_sheet(wb, ws, name);
// Write to Excel file
var wbout = XLSX.write(wb, {bookType:'xlsx', type:'binary'});
var zip = new JSZip();
zip.file("Menu.xlsx", wbout, {binary: true});
zip.file("ReadMe.txt", name)
// Get images
var img_urls = [];
for (let dish of dishes) { img_urls.push(dish[5]); }
const promises = img_urls.map(async (url) => {
const res = await fetch(url);
const blob = await res.blob();
return blob;
})
const res = await Promise.all(promises)
var img_folder = zip.folder("images")
res.forEach((blob, index) => {
var img_filename = `${index} - ${dishes[index][1]}.jpg`;
img_folder.file(img_filename, blob);
console.log(`Saving ${img_filename}`);
})
// Download ZIP file
zip.generateAsync({type:"blob"})
.then((content) => {
console.log("Saving to ZIP file");
saveAs(content, `${name}.zip`); // No Save As dialog?
console.log("ZIP file created. Web scraping complete.")
});
}
The problem applies to file types other than just the ZIP file, as using saveAs on the Excel file in the export script also resulted in the Save As dialog not displaying as well.