Première version brute
This commit is contained in:
parent
305adeb465
commit
d011746d55
@ -5,7 +5,7 @@
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<link rel="icon" href="data:,">
|
||||
|
||||
<title>Front-end template</title>
|
||||
<title>Upload ods/xlsx</title>
|
||||
|
||||
<meta name="description" content=" ">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@ -20,13 +20,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1 id="test-page">Test page</h1>
|
||||
|
||||
<p>Something should be written here:</p>
|
||||
|
||||
<section class="svelte-main"></section>
|
||||
|
||||
<p>Did it work?</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
32
package-lock.json
generated
32
package-lock.json
generated
@ -7,6 +7,9 @@
|
||||
"": {
|
||||
"name": "front-end-template",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"unzipit": "^1.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
@ -2289,12 +2292,28 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unzipit": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/unzipit/-/unzipit-1.4.3.tgz",
|
||||
"integrity": "sha512-gsq2PdJIWWGhx5kcdWStvNWit9FVdTewm4SEG7gFskWs+XCVaULt9+BwuoBtJiRE8eo3L1IPAOrbByNLtLtIlg==",
|
||||
"dependencies": {
|
||||
"uzip-module": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/uzip-module": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uzip-module/-/uzip-module-1.0.3.tgz",
|
||||
"integrity": "sha512-AMqwWZaknLM77G+VPYNZLEruMGWGzyigPK3/Whg99B3S6vGHuqsyl5ZrOv1UUF3paGK1U6PM0cnayioaryg/fA=="
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
@ -4021,12 +4040,25 @@
|
||||
"qs": "^6.4.0"
|
||||
}
|
||||
},
|
||||
"unzipit": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/unzipit/-/unzipit-1.4.3.tgz",
|
||||
"integrity": "sha512-gsq2PdJIWWGhx5kcdWStvNWit9FVdTewm4SEG7gFskWs+XCVaULt9+BwuoBtJiRE8eo3L1IPAOrbByNLtLtIlg==",
|
||||
"requires": {
|
||||
"uzip-module": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"dev": true
|
||||
},
|
||||
"uzip-module": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uzip-module/-/uzip-module-1.0.3.tgz",
|
||||
"integrity": "sha512-AMqwWZaknLM77G+VPYNZLEruMGWGzyigPK3/Whg99B3S6vGHuqsyl5ZrOv1UUF3paGK1U6PM0cnayioaryg/fA=="
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
|
||||
@ -20,5 +20,8 @@
|
||||
"sass": "^1.58.3",
|
||||
"svelte": "^4.2.9",
|
||||
"svelte-preprocess": "^5.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"unzipit": "^1.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
|
||||
export default {
|
||||
input: 'scripts/main.js',
|
||||
input: 'scripts/front-end.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'es',
|
||||
|
||||
@ -1,8 +1,31 @@
|
||||
<script>
|
||||
export let name;
|
||||
import getTableRawContentFromFile from './getTableRawContentFromFile.js'
|
||||
|
||||
const ODS_TYPE = "application/vnd.oasis.opendocument.spreadsheet";
|
||||
const XLSX_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
|
||||
let files
|
||||
|
||||
let tableRawContent;
|
||||
|
||||
/** @type {File} */
|
||||
$: file = files && files[0]
|
||||
$: console.log('file', file)
|
||||
$: tableRawContent = file && getTableRawContentFromFile(file)
|
||||
$: console.log('tableRawContent', tableRawContent)
|
||||
|
||||
</script>
|
||||
|
||||
<h2>Hello {name}! 🧝🏿</h2>
|
||||
<h1>Import fichier .ods et .xslx</h1>
|
||||
|
||||
<section>
|
||||
<label>
|
||||
Fichier à importer:
|
||||
<input bind:files type="file" id="file-input" accept="{ ['.ods', '.xlsx', ODS_TYPE, XLSX_TYPE].join(',') }" />
|
||||
</label>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@ -16,12 +39,5 @@
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #ff3e00;
|
||||
text-transform: uppercase;
|
||||
font-size: 4em;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
192
scripts/getTableRawContentFromFile.js
Normal file
192
scripts/getTableRawContentFromFile.js
Normal file
@ -0,0 +1,192 @@
|
||||
//@ts-check
|
||||
|
||||
import { unzip } from 'unzipit';
|
||||
|
||||
const parser = new DOMParser();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {Document}
|
||||
*/
|
||||
function parseXML(str){
|
||||
return parser.parseFromString(str, 'application/xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef TableCellRawContent
|
||||
* @prop {string} value
|
||||
* @prop {'float' | 'percentage' | 'currency' | 'date' | 'time' | 'boolean' | 'string' | 'b' | 'd' | 'e' | 'inlineStr' | 'n' | 's' | 'str'} type
|
||||
*
|
||||
*/
|
||||
|
||||
const ODS_TYPE = "application/vnd.oasis.opendocument.spreadsheet";
|
||||
const XLSX_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
|
||||
|
||||
/**
|
||||
* Converts a cell value to the appropriate JavaScript type based on its cell type.
|
||||
* @param {string} value - The value of the cell.
|
||||
* @param {TableCellRawContent['type']} cellType - The type of the cell.
|
||||
* @returns {any} The converted value.
|
||||
*/
|
||||
function convertCellValue(value, cellType) {
|
||||
if(value === ''){
|
||||
return ''
|
||||
}
|
||||
|
||||
switch (cellType) {
|
||||
case 'float':
|
||||
case 'percentage':
|
||||
case 'currency':
|
||||
case 'n': // number
|
||||
return parseFloat(value);
|
||||
case 'date':
|
||||
case 'd': // date
|
||||
return new Date(value);
|
||||
case 'boolean':
|
||||
case 'b': // boolean
|
||||
return value === '1' || value === 'true';
|
||||
case 's': // shared string
|
||||
case 'inlineStr': // inline string
|
||||
case 'string':
|
||||
case 'e': // error
|
||||
case 'time':
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts raw table content from an ODS file.
|
||||
* @param {File} file - The ODS file.
|
||||
* @param {Function} unzip - Function to unzip the file.
|
||||
* @param {Function} parseXML - Function to parse XML content.
|
||||
* @returns {Promise<Map<SheetName, TableCellRawContent[][]>>}
|
||||
*/
|
||||
async function getTableRawContentFromODSFile(file, unzip, parseXML) {
|
||||
const zip = await unzip(file);
|
||||
console.log('zip', zip)
|
||||
const entries = zip.entries;
|
||||
|
||||
// Extract the content.xml file which contains the spreadsheet data
|
||||
const contentXml = await entries['content.xml'].text();
|
||||
const contentDoc = parseXML(contentXml);
|
||||
|
||||
const tableMap = new Map();
|
||||
|
||||
// Navigate the XML structure to extract table data
|
||||
const tables = contentDoc.getElementsByTagName('table:table');
|
||||
|
||||
for (let table of tables) {
|
||||
const sheetName = table.getAttribute('table:name');
|
||||
const rows = table.getElementsByTagName('table:table-row');
|
||||
const sheetData = [];
|
||||
|
||||
for (let row of rows) {
|
||||
const cells = row.getElementsByTagName('table:table-cell');
|
||||
const rowData = [];
|
||||
|
||||
for (let cell of cells) {
|
||||
const cellType = cell.getAttribute('office:value-type');
|
||||
const cellValue = cellType === 'string' ? cell.textContent : cell.getAttribute('office:value');
|
||||
rowData.push({
|
||||
value: cellValue,
|
||||
type: cellType
|
||||
});
|
||||
}
|
||||
|
||||
sheetData.push(rowData);
|
||||
}
|
||||
|
||||
tableMap.set(sheetName, sheetData);
|
||||
}
|
||||
|
||||
return tableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts raw table content from an XLSX file.
|
||||
* @param {File} file - The XLSX file.
|
||||
* @param {Function} unzip - Function to unzip the file.
|
||||
* @param {Function} parseXML - Function to parse XML content.
|
||||
* @returns {Promise<Map<SheetName, TableCellRawContent[][]>>}
|
||||
*/
|
||||
async function getTableRawContentFromXSLXFile(file, unzip, parseXML) {
|
||||
const zip = await unzip(file);
|
||||
const entries = zip.entries;
|
||||
|
||||
// Read shared strings
|
||||
const sharedStringsXml = await entries['xl/sharedStrings.xml'].text();
|
||||
const sharedStringsDoc = parseXML(sharedStringsXml);
|
||||
const sharedStrings = Array.from(sharedStringsDoc.getElementsByTagName('sst')[0].getElementsByTagName('si')).map(si => si.textContent);
|
||||
|
||||
// Get sheet names and their corresponding XML files
|
||||
const workbookXml = await entries['xl/workbook.xml'].text();
|
||||
const workbookDoc = parseXML(workbookXml);
|
||||
const sheets = Array.from(workbookDoc.getElementsByTagName('sheets')[0].getElementsByTagName('sheet'));
|
||||
const sheetNames = sheets.map(sheet => sheet.getAttribute('name'));
|
||||
const sheetIds = sheets.map(sheet => sheet.getAttribute('r:id'));
|
||||
|
||||
// Read the relations to get the actual filenames for each sheet
|
||||
const workbookRelsXml = await entries['xl/_rels/workbook.xml.rels'].text();
|
||||
const workbookRelsDoc = parseXML(workbookRelsXml);
|
||||
const sheetRels = Array.from(workbookRelsDoc.getElementsByTagName('Relationship'));
|
||||
const sheetFiles = sheetIds.map(id => sheetRels.find(rel => rel.getAttribute('Id') === id).getAttribute('Target').replace('worksheets/', ''));
|
||||
|
||||
// Read each sheet's XML and extract data in parallel
|
||||
const sheetDataPs = sheetFiles.map((sheetFile, index) => (
|
||||
entries[`xl/worksheets/${sheetFile}`].text().then(sheetXml => {
|
||||
const sheetDoc = parseXML(sheetXml);
|
||||
|
||||
const rows = sheetDoc.getElementsByTagName('sheetData')[0].getElementsByTagName('row');
|
||||
const sheetData = [];
|
||||
|
||||
for (let row of rows) {
|
||||
const cells = row.getElementsByTagName('c');
|
||||
const rowData = [];
|
||||
|
||||
for (let cell of cells) {
|
||||
const cellType = cell.getAttribute('t') || 'n';
|
||||
let cellValue = cell.getElementsByTagName('v')[0]?.textContent || '';
|
||||
|
||||
if (cellType === 's') {
|
||||
cellValue = sharedStrings[parseInt(cellValue, 10)];
|
||||
}
|
||||
|
||||
rowData.push({
|
||||
value: cellValue,
|
||||
type: cellType
|
||||
});
|
||||
}
|
||||
|
||||
sheetData.push(rowData);
|
||||
}
|
||||
|
||||
return [sheetNames[index], sheetData];
|
||||
})
|
||||
));
|
||||
|
||||
return new Map(await Promise.all(sheetDataPs));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @typedef {string} SheetName */
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {File} file
|
||||
* @returns {Promise<Map<SheetName, TableCellRawContent[][]>>}
|
||||
*/
|
||||
export default function getTableRawContentFromFile(file){
|
||||
if(file.type === ODS_TYPE)
|
||||
return getTableRawContentFromODSFile(file, unzip, parseXML)
|
||||
|
||||
if(file.type === XLSX_TYPE)
|
||||
return getTableRawContentFromXSLXFile(file, unzip, parseXML)
|
||||
|
||||
throw new TypeError(`Unsupported file type: ${file.type} (${file.name})`)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user