forked from sheetjs/docs.sheetjs.com
225 lines
7.1 KiB
Markdown
225 lines
7.1 KiB
Markdown
|
---
|
||
|
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>
|