forked from sheetjs/docs.sheetjs.com
		
	HonoJS demo
This commit is contained in:
		
							parent
							
								
									3645643606
								
							
						
					
					
						commit
						0699eabbba
					
				
							
								
								
									
										232
									
								
								docz/docs/03-demos/03-net/03-server/07-hono.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										232
									
								
								docz/docs/03-demos/03-net/03-server/07-hono.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,232 @@ | ||||
| --- | ||||
| title: Sheets on Fire with HonoJS | ||||
| sidebar_label: HonoJS | ||||
| pagination_prev: demos/net/network/index | ||||
| pagination_next: demos/net/email/index | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| [HonoJS](https://hono.dev/) is a lightweight server-side framework. | ||||
| 
 | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses HonoJS and SheetJS to read and write data. We'll explore how to | ||||
| parse uploaded files in a POST request handler and respond to GET requests with | ||||
| downloadable spreadsheets. | ||||
| 
 | ||||
| The ["Complete Example"](#complete-example) section includes a complete server. | ||||
| 
 | ||||
| :::note Tested Deployments | ||||
| 
 | ||||
| This demo was last tested in the following deployments: | ||||
| 
 | ||||
| | Platform       | HonoJS  | Date       | | ||||
| |:---------------|:--------|:-----------| | ||||
| | BunJS `1.1.21` | `4.5.1` | 2024-07-27 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| The [SheetJS BunJS module](/docs/getting-started/installation/bun) can be | ||||
| imported from HonoJS server scripts. | ||||
| 
 | ||||
| ### Reading Data | ||||
| 
 | ||||
| The HonoJS body parser[^1] processes files in POST requests. The body parser | ||||
| returns an object that can be indexed by field name: | ||||
| 
 | ||||
| ```js | ||||
| /* /import route */ | ||||
| app.post('/import', async(c) => { | ||||
|   /* parse body */ | ||||
|   const body = await c.req.parseBody(); | ||||
|   /* get a file uploaded in the `upload` field */ | ||||
|   // highlight-next-line | ||||
|   const file = body["upload"]; | ||||
| 
 | ||||
|   /* `file` is a `File` objects */ | ||||
|   // ... | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| HonoJS exposes each file as a `Blob` object. The `Blob#arrayBuffer` method | ||||
| returns a Promise that resolves to an `ArrayBuffer`. That `ArrayBuffer` can be | ||||
| parsed with the SheetJS `read` method[^2]. | ||||
| 
 | ||||
| This example server responds to POST requests. The server will look for a file | ||||
| in the request body under the `"upload"` key. If a file is present, the server | ||||
| will parse the file and, generate CSV rows using the `sheet_to_csv` method[^3], | ||||
| and respond with text: | ||||
| 
 | ||||
| ```js | ||||
| import { Hono } from 'hono'; | ||||
| import { read, utils } from 'xlsx'; | ||||
| 
 | ||||
| const app = new Hono(); | ||||
| app.post('/import', async(c) => { | ||||
|   /* get file data */ | ||||
|   const body = await c.req.parseBody(); | ||||
|   const file = body["upload"]; | ||||
|   const ab = await file.arrayBuffer(); | ||||
|   /* parse */ | ||||
|   const wb = read(ab); | ||||
|   /* generate CSV */ | ||||
|   const csv = utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]); | ||||
|   return c.text(csv); | ||||
| }); | ||||
| export default app; | ||||
| ``` | ||||
| 
 | ||||
| ### Writing Data | ||||
| 
 | ||||
| Given a SheetJS workbook object, the `write` method using `type: "buffer"`[^4] | ||||
| generates data objects which can be passed to the response `body` method. | ||||
| 
 | ||||
| This example server responds to GET requests. The server will generate a SheetJS | ||||
| worksheet object from an array of arrays[^5], build up a new workbook using the | ||||
| `book_new`[^6] utility method, generate a XLSX file using `write`, and send the | ||||
| file with appropriate headers to download `SheetJSHonoJS.xlsx`: | ||||
| 
 | ||||
| ```js | ||||
| import { Hono } from 'hono'; | ||||
| import { utils, write } from "xlsx"; | ||||
| 
 | ||||
| const app = new Hono(); | ||||
| app.get("/export", (c) => { | ||||
|   /* generate SheetJS workbook object */ | ||||
|   var ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]); | ||||
|   var wb = utils.book_new(ws, "Data"); | ||||
|   /* generate buffer */ | ||||
|   var buf = write(wb, {type: "buffer", bookType: "xlsx"}); | ||||
|   /* set headers */ | ||||
|   c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"'); | ||||
|   c.header('Content-Type', 'application/vnd.ms-excel'); | ||||
|   /* export buffer */ | ||||
|   return c.body(buf); | ||||
| }); | ||||
| export default app; | ||||
| ``` | ||||
| 
 | ||||
| ## Complete Example | ||||
| 
 | ||||
| This example creates a simple server that stores an array of arrays. There are | ||||
| three server endpoints: | ||||
| 
 | ||||
| - `/import` POST request expects a file in the `upload` field. It will parse the | ||||
| file, update the internal array of arrays, and responds with CSV data. | ||||
| 
 | ||||
| - `/export` GET request generates a workbook from the internal array of arrays. | ||||
| It will respond with XLSX data and initiate a download to `SheetJSHonoJS.xlsx` . | ||||
| 
 | ||||
| - `/json` GET request responds with the internal state. | ||||
| 
 | ||||
| 1) Create a new BunJS + HonoJS project: | ||||
| 
 | ||||
| ```bash | ||||
| bun create hono sheetjs-hono --template bun --install --pm bun | ||||
| cd sheetjs-hono | ||||
| ``` | ||||
| 
 | ||||
| 2) Install the [SheetJS BunJS module](/docs/getting-started/installation/bun): | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| bun i xlsx@https://sheet.lol/balls/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 3) Save the following script to `src/index.ts`: | ||||
| 
 | ||||
| ```ts title="src/index.ts" | ||||
| import { Hono } from 'hono'; | ||||
| import { read, write, utils } from 'xlsx'; | ||||
| 
 | ||||
| const app = new Hono(); | ||||
| let data = ["SheetJS".split(""), [5,4,3,3,7,9,5]]; | ||||
| 
 | ||||
| app.get('/export', (c) => { | ||||
|   const ws = utils.aoa_to_sheet(data); | ||||
|   const wb = utils.book_new(ws, "SheetJSHono"); | ||||
|   const buf = write(wb, { type: "buffer", bookType: "xlsx" }); | ||||
|   c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"'); | ||||
|   c.header('Content-Type', 'application/vnd.ms-excel'); | ||||
|   return c.body(buf); | ||||
| }); | ||||
| 
 | ||||
| app.post('/import', async(c) => { | ||||
|   const body = await c.req.parseBody(); | ||||
|   const file = body["upload"]; | ||||
|   const ab = await file.arrayBuffer(); | ||||
|   const wb = read(ab); | ||||
|   data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header:1 }); | ||||
|   return c.text(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])); | ||||
| }); | ||||
| 
 | ||||
| app.get('/json', (c) => c.json(data)); | ||||
| export default app; | ||||
| ``` | ||||
| 
 | ||||
| 4) Run the server: | ||||
| 
 | ||||
| ```bash | ||||
| bun run dev | ||||
| ``` | ||||
| 
 | ||||
| The process will display a URL (typically `http://localhost:3000`): | ||||
| 
 | ||||
| ```text | ||||
| % bun run dev | ||||
| $ bun run --hot src/index.ts | ||||
| // highlight-next-line | ||||
| Started server http://localhost:3000 | ||||
| ``` | ||||
| 
 | ||||
| 5) Test exports by opening `http://localhost:3000/export` in your browser. | ||||
| 
 | ||||
| The page should attempt to download `SheetJSHonoJS.xlsx` . Save the download and | ||||
| open the new file. The contents should match the original data: | ||||
| 
 | ||||
| <table> | ||||
|   <tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr> | ||||
|   <tr><td>5</td><td>4</td><td>3</td><td>3</td><td>7</td><td>9</td><td>5</td></tr> | ||||
| </table> | ||||
| 
 | ||||
| 6) Test imports using https://docs.sheetjs.com/pres.numbers . The commands | ||||
| should be run in a new terminal window: | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://docs.sheetjs.com/pres.numbers | ||||
| curl -X POST -F upload=@pres.numbers http://localhost:3000/import | ||||
| ``` | ||||
| 
 | ||||
| The terminal will display CSV rows generated from the first worksheet: | ||||
| 
 | ||||
| ```text title="Expected output" | ||||
| Name,Index | ||||
| Bill Clinton,42 | ||||
| GeorgeW Bush,43 | ||||
| Barack Obama,44 | ||||
| Donald Trump,45 | ||||
| Joseph Biden,46 | ||||
| ``` | ||||
| 
 | ||||
| 7) Confirm the state was updated by loading `http://localhost:3000/json` : | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO http://localhost:3000/json | ||||
| ``` | ||||
| 
 | ||||
| The terminal will display the worksheet data in an array of arrays: | ||||
| 
 | ||||
| ```json title="Expected output" | ||||
| [["Name","Index"],["Bill Clinton",42],["GeorgeW Bush",43],["Barack Obama",44],["Donald Trump",45],["Joseph Biden",46]] | ||||
| ``` | ||||
| 
 | ||||
| [^1]: See ["parseBody()"](https://hono.dev/docs/api/request#parsebody) in the HonoJS documentation. | ||||
| [^2]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^3]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#delimiter-separated-output) | ||||
| [^4]: See [`write` in "Writing Files"](/docs/api/write-options) | ||||
| [^5]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input) | ||||
| [^6]: See [`book_new` in "Utilities"](/docs/api/utilities/wb) | ||||
| @ -407,7 +407,7 @@ Function arguments are separated with commas. For example, the Spanish Excel | ||||
| formula `=CONTAR(A1:C3;B4:D6)` is equivalent to the SheetJS formula string | ||||
| `COUNT(A1:A3,B4:D6)` | ||||
| 
 | ||||
| [JSON Translation table](https://oss.sheetjs.com/notes/fmla/table.json). | ||||
| [JSON Translation table](https://docs.sheetjs.com/fmla/table.json). | ||||
| 
 | ||||
| <details open> | ||||
|   <summary><b>Function Name Translator</b> (click to hide)</summary> | ||||
| @ -422,7 +422,7 @@ function Translator(props) { | ||||
|   /* Fetch and display formula */ | ||||
|   React.useEffect(() => { (async() => { | ||||
|     /* Fetch data */ | ||||
|     const json = await (await fetch("https://oss.sheetjs.com/notes/fmla/table.json")).json(); | ||||
|     const json = await (await fetch("/fmla/table.json")).json(); | ||||
|     setLocales(Object.keys(json)); | ||||
|     setData(json); | ||||
|     setNames(json.en); | ||||
|  | ||||
| @ -46,7 +46,7 @@ These instructions were tested on the following platforms: | ||||
| | MacOS 14.4 (x64)              | `darwin-x64` | 2024-07-12 | | ||||
| | MacOS 14.5 (ARM64)            | `darwin-arm` | 2024-05-23 | | ||||
| | Windows 10 (x64) + WSL Ubuntu | `win10-x64`  | 2024-07-12 | | ||||
| | Windows 11 (x64) + WSL Ubuntu | `win11-x64`  | 2024-05-10 | | ||||
| | Windows 11 (x64) + WSL Ubuntu | `win11-x64`  | 2024-07-26 | | ||||
| | Windows 11 (ARM) + WSL Ubuntu | `win11-arm`  | 2024-05-23 | | ||||
| 
 | ||||
| With some additional dependencies, the unminified scripts are reproducible and | ||||
|  | ||||
							
								
								
									
										1
									
								
								docz/static/fmla/table.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								docz/static/fmla/table.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user