drash
This commit is contained in:
parent
913538b2ed
commit
08b7493c34
@ -417,7 +417,7 @@ for(var i = 0; i < stmts.length; ++i) await new Promise((res, rej) => {
|
||||
|
||||
The result of a SQL SELECT statement is a `SQLResultSet`. The `rows` property
|
||||
is a `SQLResultSetRowList`. It is an "array-like" structure that has `length`
|
||||
and properies like `0`, `1`, etc. However, this is not a real Array object.
|
||||
and properties like `0`, `1`, etc. However, this is not a real Array object.
|
||||
A real Array can be created using `Array.from`:
|
||||
|
||||
```js
|
||||
|
@ -223,9 +223,7 @@ var dataset = Float32Array.from(column);
|
||||
|
||||
`XLSX.utils.aoa_to_sheet` can generate a worksheet from an array of arrays.
|
||||
ML libraries typically provide APIs to pull an array of arrays, but it will
|
||||
be transponsed
|
||||
a row-major array of arrays. To export multiple data
|
||||
sets, "transpose" the data:
|
||||
be transposed. To export multiple data sets, manually "transpose" the data:
|
||||
|
||||
```js
|
||||
/* assuming data is an array of typed arrays */
|
||||
|
@ -7,7 +7,7 @@ import current from '/version.js';
|
||||
|
||||
Over the years, many frameworks have been released. Some were popular years ago
|
||||
but have waned in recent years. There are still many deployments using these
|
||||
frameworks and it is oftentimes esasier to continue maintenance than to rewrite
|
||||
frameworks and it is oftentimes easier to continue maintenance than to rewrite
|
||||
using modern web techniques.
|
||||
|
||||
SheetJS libraries strive to maintain broad browser and JS engine compatibility.
|
||||
|
@ -1370,6 +1370,15 @@ id,content
|
||||
|
||||
## Ionic
|
||||
|
||||
:::note
|
||||
|
||||
This demo was tested on an Intel Mac on 2022 August 18 with Cordova backend.
|
||||
The file integration uses `@ionic-native/file` version `5.36.0`.
|
||||
|
||||
The iOS simulator runs iOS 15.5 on an iPod Touch 7th Gen.
|
||||
|
||||
:::
|
||||
|
||||
:::warning Telemetry
|
||||
|
||||
Before starting this demo, manually disable telemetry. On Linux and macOS:
|
||||
@ -1400,8 +1409,7 @@ npx @capacitor/cli telemetry
|
||||
|
||||
:::caution
|
||||
|
||||
The latest version of Ionic uses CapacitorJS. These notes are for older apps
|
||||
using Cordova
|
||||
The latest version of Ionic uses CapacitorJS. These notes are for Cordova apps.
|
||||
|
||||
:::
|
||||
|
||||
@ -1432,3 +1440,77 @@ let blob = new Blob([wbout], {type: 'application/octet-stream'});
|
||||
this.file.writeFile(url, filename, blob, {replace: true});
|
||||
```
|
||||
|
||||
### Demo
|
||||
|
||||
The demo uses Cordova.
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
0) Disable telemetry as noted in the warning.
|
||||
|
||||
Install required global dependencies:
|
||||
|
||||
```bash
|
||||
npm i -g cordova-res @angular/cli native-run
|
||||
```
|
||||
|
||||
Follow the [React Native demo](#demo) to ensure iOS and Android sims are ready.
|
||||
|
||||
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @ionic/cli start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-link --confirm
|
||||
```
|
||||
|
||||
If a prompt discusses Cordova and Capacitor, enter `Yes` to continue.
|
||||
|
||||
If a prompt asks about creating an Ionic account, enter `N` to opt out.
|
||||
|
||||
2) Set up Cordova:
|
||||
|
||||
```bash
|
||||
npx @ionic/cli cordova platform add ios --confirm
|
||||
npx @ionic/cli cordova plugin add cordova-plugin-file
|
||||
npm install --save @ionic-native/core @ionic-native/file @ionic/cordova-builders
|
||||
```
|
||||
|
||||
3) Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
|
||||
```
|
||||
|
||||
4) Add `@ionic-native/file` to the module. Differences highlighted below:
|
||||
|
||||
```ts title="src/app/app.module.ts"
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
// highlight-next-line
|
||||
import { File } from '@ionic-native/file/ngx';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
|
||||
|
||||
// highlight-next-line
|
||||
providers: [File, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
5) Download [`home.page.ts`](pathname:///ionic/home.page.ts) and replace:
|
||||
|
||||
```bash
|
||||
curl -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
|
||||
```
|
||||
|
||||
6) Test the app:
|
||||
|
||||
```bash
|
||||
npx @ionic/cli cordova emulate ios
|
||||
```
|
||||
|
||||
</details>
|
@ -71,15 +71,13 @@ console.log(data);
|
||||
A component will typically map over the data. The following example generates
|
||||
a TABLE with a row for each President:
|
||||
|
||||
```tsx title="src/SheetJSReactAoO.tsx"
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
interface President { Name: string; Index: number; }
|
||||
```jsx title="src/SheetJSReactAoO.js"
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactAoO() {
|
||||
/* the component state is an array of presidents */
|
||||
const [pres, setPres] = useState<President[]>([]);
|
||||
const [pres, setPres] = useState([]);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
@ -87,11 +85,20 @@ export default function SheetJSReactAoO() {
|
||||
// highlight-start
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_json<President>(ws); // generate objects
|
||||
const data = utils.sheet_to_json(ws); // generate objects
|
||||
setPres(data); // update state
|
||||
// highlight-end
|
||||
})(); }, []);
|
||||
|
||||
/* get state data and export to XLSX */
|
||||
const exportFile = useCallback(() => {
|
||||
// highlight-next-line
|
||||
const ws = utils.json_to_sheet(pres);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "Data");
|
||||
writeFileXLSX(wb, "SheetJSReactAoO.xlsx");
|
||||
}, [pres]);
|
||||
|
||||
return (<table><thead><th>Name</th><th>Index</th></thead><tbody>
|
||||
{ /* generate row for each president */
|
||||
// highlight-start
|
||||
@ -101,7 +108,9 @@ export default function SheetJSReactAoO() {
|
||||
</tr>))
|
||||
// highlight-end
|
||||
}
|
||||
</tbody></table>);
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button onClick={exportFile}>Export XLSX</button>
|
||||
</td></tfoot></table>);
|
||||
}
|
||||
```
|
||||
|
||||
@ -115,13 +124,15 @@ The `sheet_to_html` function generates HTML that is aware of merges and other
|
||||
worksheet features. React `dangerouslySetInnerHTML` attribute allows code to
|
||||
set the `innerHTML` attribute, effectively inserting the code into the page:
|
||||
|
||||
```tsx title="src/SheetJSReactHTML.tsx"
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { read, utils } from 'xlsx';
|
||||
```jsx title="src/SheetJSReactHTML.js"
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactHTML() {
|
||||
/* the component state is an HTML string */
|
||||
const [html, setHtml] = useState<string>("");
|
||||
const [html, setHtml] = useState("");
|
||||
/* the ref is used in export */
|
||||
const tbl = useRef(null);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
@ -134,8 +145,20 @@ export default function SheetJSReactHTML() {
|
||||
// highlight-end
|
||||
})(); }, []);
|
||||
|
||||
/* get live table and export to XLSX */
|
||||
const exportFile = useCallback(() => {
|
||||
// highlight-start
|
||||
const elt = tbl.current.getElementsByTagName("TABLE")[0];
|
||||
const wb = utils.table_to_book(elt);
|
||||
// highlight-end
|
||||
writeFileXLSX(wb, "SheetJSReactHTML.xlsx");
|
||||
}, [tbl]);
|
||||
|
||||
return ( <>
|
||||
<button onClick={exportFile}>Export XLSX</button>
|
||||
// highlight-next-line
|
||||
return ( <div dangerouslySetInnerHTML={{ __html: html }} />);
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
</>);
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -74,7 +74,7 @@ a TABLE with a row for each President:
|
||||
```html title="src/SheetJSVueAoO.vue"
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { read, utils } from 'xlsx';
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
const rows = ref([]);
|
||||
|
||||
@ -89,6 +89,14 @@ onMounted(async() => {
|
||||
/* update data */
|
||||
rows.value = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
});
|
||||
|
||||
/* get state data and export to XLSX */
|
||||
function exportFile() {
|
||||
const ws = utils.json_to_sheet(rows.value);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "Data");
|
||||
writeFileXLSX(wb, "SheetJSVueAoO.xlsx");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -97,7 +105,9 @@ onMounted(async() => {
|
||||
<td>{{ row.Name }}</td>
|
||||
<td>{{ row.Index }}</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button @click="exportFile">Export XLSX</button>
|
||||
</td></tfoot></table>
|
||||
</template>
|
||||
```
|
||||
|
||||
@ -114,9 +124,10 @@ attribute, effectively inserting the code into the page:
|
||||
```html title="src/SheetJSVueHTML.vue"
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { read, utils } from 'xlsx';
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
const html = ref("");
|
||||
const tableau = ref();
|
||||
|
||||
onMounted(async() => {
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
@ -129,10 +140,17 @@ onMounted(async() => {
|
||||
/* update data */
|
||||
html.value = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
});
|
||||
|
||||
/* get live table and export to XLSX */
|
||||
function exportFile() {
|
||||
const wb = utils.table_to_book(tableau.value.getElementsByTagName("TABLE")[0])
|
||||
writeFileXLSX(wb, "SheetJSVueHTML.xlsx");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-html="html"></div>
|
||||
<button @click="exportFile">Export XLSX</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
|
@ -16,7 +16,7 @@ and TypeScript familiarity is assumed.
|
||||
Other demos cover general Angular deployments, including:
|
||||
|
||||
- [iOS and Android applications powered by NativeScript](./mobile#nativescript)
|
||||
- [iOS and Android applications powered by ionic](./mobile#nativescript)
|
||||
- [iOS and Android applications powered by ionic](./mobile#ionic)
|
||||
|
||||
:::warning
|
||||
|
||||
@ -80,12 +80,12 @@ console.log(data);
|
||||
]
|
||||
```
|
||||
|
||||
A component will typically loop over the data uaing `*ngFor`. The following
|
||||
A component will typically loop over the data using `*ngFor`. The following
|
||||
example generates a TABLE with a row for each President:
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component } from '@angular/core';
|
||||
import { read, utils } from 'xlsx';
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
interface President { Name: string; Index: number };
|
||||
|
||||
@ -101,7 +101,9 @@ interface President { Name: string; Index: number };
|
||||
<td>{{row.Index}}</td>
|
||||
</tr>
|
||||
// highlight-end
|
||||
</tbody>
|
||||
</tbody><tfoot>
|
||||
<button (click)="onSave()">Export XLSX</button>
|
||||
</tfoot>
|
||||
</table></div>
|
||||
`
|
||||
})
|
||||
@ -122,6 +124,14 @@ export class AppComponent {
|
||||
this.rows = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
|
||||
|
||||
})(); }
|
||||
/* get state data and export to XLSX */
|
||||
onSave(): void {
|
||||
// highlight-next-line
|
||||
const ws = utils.json_to_sheet(this.rows);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "Data");
|
||||
writeFileXLSX(wb, "SheetJSAngularAoO.xlsx");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -137,20 +147,22 @@ and should therefore be safe to pass to an `innerHTML`-bound variable, but the
|
||||
`DomSanitizer` approach is strongly recommended:
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
// highlight-next-line
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { read, utils } from 'xlsx';
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
// highlight-next-line
|
||||
template: `<div class="content" role="main" [innerHTML]="html"></div>`
|
||||
template: `<div class="content" role="main" [innerHTML]="html" #tableau></div>
|
||||
<button (click)="onSave()">Export XLSX</button>`
|
||||
})
|
||||
export class AppComponent {
|
||||
// highlight-start
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
html: SafeHtml = "";
|
||||
@ViewChild('tableau') tabeller!: ElementRef<HTMLDivElement>;
|
||||
// highlight-end
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
@ -166,6 +178,14 @@ export class AppComponent {
|
||||
this.html = this.sanitizer.bypassSecurityTrustHtml(h);
|
||||
// highlight-end
|
||||
})(); }
|
||||
/* get live table and export to XLSX */
|
||||
onSave(): void {
|
||||
// highlight-start
|
||||
const elt = this.tabeller.nativeElement.getElementsByTagName("TABLE")[0];
|
||||
const wb = utils.table_to_book(elt);
|
||||
// highlight-end
|
||||
writeFileXLSX(wb, "SheetJSAngularHTML.xlsx");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
225
docz/docs/03-demos/24-server.md
Normal file
225
docz/docs/03-demos/24-server.md
Normal file
@ -0,0 +1,225 @@
|
||||
---
|
||||
sidebar_position: 23
|
||||
title: HTTP Server Processing
|
||||
---
|
||||
|
||||
Server-Side JS platforms like NodeJS and Deno have built-in APIs for listening
|
||||
on network interfaces. They provide wrappers for requests and responses.
|
||||
|
||||
## Overview
|
||||
|
||||
#### Reading Data
|
||||
|
||||
Typically servers receive form data with content type `multipart/form-data` or
|
||||
`application/x-www-form-urlencoded`. The platforms themselves typically do not
|
||||
provide "body parsing" functions, instead leaning on the community to supply
|
||||
modules to take the encoded data and split into form fields and files.
|
||||
|
||||
NodeJS servers typically use a parser like `formidable`. In the example below,
|
||||
`formidable` will write to file and `XLSX.readFile` will read the file:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx"); // This is using the CommonJS build
|
||||
var formidable = require("formidable");
|
||||
|
||||
require("http").createServer(function(req, res) {
|
||||
if(req.method !== "POST") return res.end("");
|
||||
|
||||
/* parse body and implement logic in callback */
|
||||
// highlight-next-line
|
||||
(new formidable.IncomingForm()).parse(req, function(err, fields, files) {
|
||||
/* if successful, files is an object whose keys are param names */
|
||||
// highlight-next-line
|
||||
var file = files["upload"]; // <input type="file" id="upload" name="upload">
|
||||
/* file.path is a location in the filesystem, usually in a temp folder */
|
||||
// highlight-next-line
|
||||
var wb = XLSX.readFile(file.filepath);
|
||||
// print the first worksheet back as a CSV
|
||||
res.end(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
|
||||
});
|
||||
}).listen(process.env.PORT || 3000);
|
||||
```
|
||||
|
||||
`XLSX.read` will accept NodeJS buffers as well as `Uint8Array`, Base64 strings,
|
||||
binary strings, and plain Arrays of bytes. This covers the interface types of
|
||||
a wide variety of frameworks.
|
||||
|
||||
#### Writing Data
|
||||
|
||||
Typically server libraries use a response API that accepts `Uint8Array` data.
|
||||
`XLSX.write` with the option `type: "buffer"` will generate data. To force the
|
||||
response to be treated as an attachment, set the `Content-Disposition` header:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx"); // This is using the CommonJS build
|
||||
|
||||
require("http").createServer(function(req, res) {
|
||||
if(req.method !== "GET") return res.end("");
|
||||
var wb = XLSX.read("S,h,e,e,t,J,S\n5,4,3,3,7,9,5", {type: "binary"});
|
||||
// highlight-start
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="SheetJS.xlsx"');
|
||||
res.end(XLSX.write(wb, {type:"buffer", bookType: "xlsx"}));
|
||||
// highlight-end
|
||||
}).listen(process.env.PORT || 3000);
|
||||
```
|
||||
|
||||
## Deno
|
||||
|
||||
:::warning
|
||||
|
||||
Many hosted services like Deno Deploy do not offer filesystem access.
|
||||
|
||||
This breaks web frameworks that use the filesystem in body parsing.
|
||||
|
||||
:::
|
||||
|
||||
Deno provides the basic elements to implement a server. It does not provide a
|
||||
body parser out of the box.
|
||||
|
||||
### Drash
|
||||
|
||||
In testing, [Drash](https://drash.land/drash/) had an in-memory body parser
|
||||
which could handle file uploads on hosted services like Deno Deploy.
|
||||
|
||||
The service <https://s2c.sheetjs.com> is hosted on Deno Deploy using Drash!
|
||||
|
||||
_Reading Data_
|
||||
|
||||
`Request#bodyParam` reads body parameters. For uploaded files, the `content`
|
||||
property is a `Uint8Array`:
|
||||
|
||||
```ts
|
||||
// @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts"
|
||||
import { read, utils } from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
|
||||
|
||||
import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts";
|
||||
|
||||
class ParseResource extends Drash.Resource {
|
||||
public paths = ["/"];
|
||||
|
||||
public POST(request: Drash.Request, response: Drash.Response) {
|
||||
// assume a form upload like <input type="file" id="upload" name="upload">
|
||||
// highlight-next-line
|
||||
const file = request.bodyParam<Drash.Types.BodyFile>("upload");
|
||||
if (!file) throw new Error("File is required!");
|
||||
// highlight-next-line
|
||||
var wb = read(file.content, {type: "buffer"});
|
||||
return response.html( utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
_Writing Data_
|
||||
|
||||
Headers are manually set with `Response#headers.set` while the raw body is set
|
||||
with `Response#send`:
|
||||
|
||||
```ts
|
||||
// @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts"
|
||||
import { read, utils } from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
|
||||
|
||||
import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts";
|
||||
|
||||
class WriteResource extends Drash.Resource {
|
||||
public paths = ["/export"];
|
||||
|
||||
public GET(request: Drash.Request, response: Drash.Response): void {
|
||||
// create some fixed workbook
|
||||
const data = ["SheetJS".split(""), [5,4,3,3,7,9,5]];
|
||||
const ws = utils.aoa_to_sheet(data);
|
||||
const wb = utils.book_new(); utils.book_append_sheet(wb, ws, "data");
|
||||
// write the workbook to XLSX as a Uint8Array
|
||||
// highlight-next-line
|
||||
const file = write(wb, { bookType: "xlsx", type: "buffer"});
|
||||
// set headers
|
||||
response.headers.set("Content-Disposition", 'attachment; filename="SheetJSDrash.xlsx"');
|
||||
// send data
|
||||
// highlight-next-line
|
||||
return response.send("application/vnd.ms-excel", file);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Save the following script to `SheetJSDrash.ts`:
|
||||
|
||||
```ts title="SheetJSDrash.ts"
|
||||
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
// @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts"
|
||||
import { read, utils, set_cptable } from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
|
||||
import * as cptable from 'https://cdn.sheetjs.com/xlsx-latest/package/dist/cpexcel.full.mjs';
|
||||
set_cptable(cptable);
|
||||
|
||||
import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts";
|
||||
|
||||
class ParseResource extends Drash.Resource {
|
||||
public paths = ["/"];
|
||||
|
||||
public POST(request: Drash.Request, response: Drash.Response) {
|
||||
const file = request.bodyParam<Drash.Types.BodyFile>("file");
|
||||
if (!file) throw new Error("File is required!");
|
||||
var wb = read(file.content, {type: "buffer"});
|
||||
return response.html( utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]));
|
||||
}
|
||||
|
||||
public GET(request: Drash.Request, response: Drash.Response): void {
|
||||
return response.html(`\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SheetJS Spreadsheet to HTML Conversion Service</title>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<pre><h3><a href="//sheetjs.com/">SheetJS</a> Spreadsheet Conversion Service</h3>
|
||||
<b>API</b>
|
||||
|
||||
Send a POST request to https://s2c.sheetjs.com/ with the file in the "file" body parameter:
|
||||
|
||||
$ curl -X POST -F"file=@test.xlsx" https://s2c.sheetjs.com/
|
||||
|
||||
The response will be an HTML TABLE generated from the first worksheet.
|
||||
|
||||
<b>Try it out!</b><form action="/" method="post" enctype="multipart/form-data">
|
||||
|
||||
<input type="file" name="file" />
|
||||
|
||||
Use the file input element to select a file, then click "Submit"
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</pre>
|
||||
</body>
|
||||
</html>`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const server = new Drash.Server({
|
||||
hostname: "",
|
||||
port: 3000,
|
||||
protocol: "http",
|
||||
resources: [ ParseResource ],
|
||||
});
|
||||
|
||||
server.run();
|
||||
|
||||
console.log(`Server running at ${server.address}.`);
|
||||
```
|
||||
|
||||
2) Run the server:
|
||||
|
||||
```bash
|
||||
deno run --allow-net SheetJSDrash.ts
|
||||
```
|
||||
|
||||
3) Download the test file <https://sheetjs.com/pres.numbers>
|
||||
|
||||
4) Open http://localhost:3000/ in your browser.
|
||||
|
||||
Click "Choose File" and select `pres.numbers`. Then click "Submit"
|
||||
|
||||
The page should show the contents of the file as an HTML table.
|
||||
|
||||
</details>
|
@ -37,6 +37,7 @@ The demo projects include small runnable examples and short explainers.
|
||||
- [`Command-Line Tools`](./cli)
|
||||
- [`iOS and Android Mobile Applications`](./mobile)
|
||||
- [`NodeJS Server-Side Processing`](https://github.com/SheetJS/SheetJS/tree/master/demos/server/)
|
||||
- [`Deno Server-Side Processing`](./server#deno)
|
||||
- [`Content Management and Static Sites`](./content)
|
||||
- [`Electron`](./desktop#electron)
|
||||
- [`NW.js`](./desktop#nwjs)
|
||||
|
@ -48,7 +48,7 @@ then `AAA`. Some sample values, along with SheetJS column indices, are listed:
|
||||
| Second | `B` | `1` |
|
||||
| 26th | `Z` | `25` |
|
||||
| 27th | `AA` | `26` |
|
||||
| 702st | `ZZ` | `701` |
|
||||
| 702nd | `ZZ` | `701` |
|
||||
| 703rd | `AAA` | `702` |
|
||||
| 16384th | `XFD` | `16383` |
|
||||
|
||||
|
@ -98,8 +98,8 @@ pixels. When the pixel and character counts do not align, Excel rounds values.
|
||||
XLSX internally stores column widths in a nebulous "Max Digit Width" form. The
|
||||
Max Digit Width is the width of the largest digit when rendered (generally the
|
||||
"0" character is the widest). The internal width must be an integer multiple of
|
||||
the the width divided by 256. ECMA-376 describes a formula for converting
|
||||
between pixels and the internal width. This represents a hybrid approach.
|
||||
the width divided by 256. ECMA-376 describes a formula for converting between
|
||||
pixels and the internal width. This represents a hybrid approach.
|
||||
|
||||
Read functions attempt to populate all three properties. Write functions will
|
||||
try to cycle specified values to the desired type. In order to avoid potential
|
||||
|
@ -269,7 +269,7 @@ function SheetJSHeaderOrder() {
|
||||
|
||||
### HTML Table Input
|
||||
|
||||
**Create a worksheet or workbook from an HTML DOM TABLE**
|
||||
**Create a worksheet or workbook from a HTML DOM TABLE**
|
||||
|
||||
```js
|
||||
var ws = XLSX.utils.table_to_sheet(elt, opts);
|
||||
|
125
docz/static/ionic/home.page.ts
Normal file
125
docz/static/ionic/home.page.ts
Normal file
@ -0,0 +1,125 @@
|
||||
/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
import { Component } from '@angular/core';
|
||||
import { File } from '@ionic-native/file/ngx';
|
||||
import * as XLSX from 'xlsx';
|
||||
|
||||
type AOA = any[][];
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
//templateUrl: 'home.page.html',
|
||||
styleUrls: ['home.page.scss'],
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>SheetJS Ionic Demo</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content [fullscreen]="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title>SheetJS Demo</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row *ngFor="let row of data">
|
||||
<ion-col *ngFor="let val of row">
|
||||
{{val}}
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer padding>
|
||||
<input type="file" (change)="onFileChange($event)" multiple="false" />
|
||||
<button ion-button color="secondary" (click)="import()">Import Data</button>
|
||||
<button ion-button color="secondary" (click)="export()">Export Data</button>
|
||||
</ion-footer>
|
||||
`
|
||||
})
|
||||
|
||||
export class HomePage {
|
||||
data: any[][] = [[1,2,3],[4,5,6]];
|
||||
constructor(public file: File) {}
|
||||
|
||||
read(ab: ArrayBuffer) {
|
||||
/* read workbook */
|
||||
const wb: XLSX.WorkBook = XLSX.read(new Uint8Array(ab), {type: 'array'});
|
||||
|
||||
/* grab first sheet */
|
||||
const wsname: string = wb.SheetNames[0];
|
||||
const ws: XLSX.WorkSheet = wb.Sheets[wsname];
|
||||
|
||||
/* save data */
|
||||
this.data = (XLSX.utils.sheet_to_json(ws, {header: 1}) as AOA);
|
||||
};
|
||||
|
||||
write(): XLSX.WorkBook {
|
||||
/* generate worksheet */
|
||||
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
|
||||
|
||||
/* generate workbook and add the worksheet */
|
||||
const wb: XLSX.WorkBook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
|
||||
|
||||
return wb;
|
||||
};
|
||||
|
||||
/* File Input element for browser */
|
||||
onFileChange(evt: any) {
|
||||
/* wire up file reader */
|
||||
const target: DataTransfer = (evt.target as DataTransfer);
|
||||
if (target.files.length !== 1) { throw new Error('Cannot use multiple files'); }
|
||||
const reader: FileReader = new FileReader();
|
||||
reader.onload = (e: any) => {
|
||||
const ab: ArrayBuffer = e.target.result;
|
||||
this.read(ab);
|
||||
};
|
||||
reader.readAsArrayBuffer(target.files[0]);
|
||||
};
|
||||
|
||||
/* Import button for mobile */
|
||||
async import() {
|
||||
try {
|
||||
const target: string = this.file.documentsDirectory || this.file.externalDataDirectory || this.file.dataDirectory || '';
|
||||
const dentry = await this.file.resolveDirectoryUrl(target);
|
||||
const url: string = dentry.nativeURL || '';
|
||||
alert(`Attempting to read SheetJSIonic.xlsx from ${url}`);
|
||||
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, 'SheetJSIonic.xlsx');
|
||||
this.read(ab);
|
||||
} catch(e) {
|
||||
const m: string = e.message;
|
||||
alert(m.match(/It was determined/) ? 'Use File Input control' : `Error: ${m}`);
|
||||
}
|
||||
};
|
||||
|
||||
/* Export button */
|
||||
async export() {
|
||||
const wb: XLSX.WorkBook = this.write();
|
||||
const filename = 'SheetJSIonic.xlsx';
|
||||
try {
|
||||
/* generate Blob */
|
||||
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||
|
||||
/* find appropriate path for mobile */
|
||||
const target: string = this.file.documentsDirectory || this.file.externalDataDirectory || this.file.dataDirectory || '';
|
||||
const dentry = await this.file.resolveDirectoryUrl(target);
|
||||
const url: string = dentry.nativeURL || '';
|
||||
|
||||
/* attempt to save blob to file */
|
||||
await this.file.writeFile(url, filename, wbout, {replace: true});
|
||||
alert(`Wrote to SheetJSIonic.xlsx in ${url}`);
|
||||
} catch(e) {
|
||||
if(e.message.match(/It was determined/)) {
|
||||
/* in the browser, use writeFile */
|
||||
XLSX.writeFile(wb, filename);
|
||||
} else {
|
||||
alert(`Error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user