Skip to content

Commit

Permalink
feat(example-file-upload-download): add file download support
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Mar 12, 2020
1 parent 1e768e1 commit 12afd6b
Show file tree
Hide file tree
Showing 34 changed files with 260 additions and 171 deletions.
2 changes: 1 addition & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
/packages/cli/generators/controller @deepakrkris @emonddr @hacksparrow
/examples/hello-world @deepakrkris @emonddr @hacksparrow
/examples/express-composition @deepakrkris @emonddr @hacksparrow
/examples/file-upload @deepakrkris @emonddr @hacksparrow @raymondfeng
/examples/file-upload-download @deepakrkris @emonddr @hacksparrow @raymondfeng

#
# OpenAPI (server-side)
Expand Down
2 changes: 1 addition & 1 deletion docs/site/Examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ LoopBack 4 comes with the following example projects:
An example showing how to compose an application from component and
controllers, interceptors, and observers.

- **[file-upload](https://github.com/strongloop/loopback-next/tree/master/examples/file-upload)**:
- **[file-upload](https://github.com/strongloop/loopback-next/tree/master/examples/file-upload-download)**:
An example showing how to expose APIs to upload files.

- **[loopback4-example-shopping](https://github.com/strongloop/loopback4-example-shopping)**:
Expand Down
2 changes: 1 addition & 1 deletion docs/site/MONOREPO.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The [loopback-next](https://github.com/strongloop/loopback-next) repository uses
| [example-access-control-migration](https://github.com/strongloop/loopback-next/tree/master/examples/access-control-migration) | @loopback/example-access-control-migration | An access control example migrated from the LoopBack 3 repository `loopback-example-access-control` |
| [example-context](https://github.com/strongloop/loopback-next/tree/master/examples/context) | @loopback/example-context | Standalone examples to illustrate features provided by @loopback/context |
| [example-express-composition](https://github.com/strongloop/loopback-next/tree/master/examples/express-composition) | @loopback/example-express-composition | A simple Express application that uses LoopBack 4 REST API |
| [example-file-upload](https://github.com/strongloop/loopback-next/tree/master/examples/file-upload) | @loopback/example-file-upload | An example showing how to expose APIs to upload files |
| [example-file-upload-download](https://github.com/strongloop/loopback-next/tree/master/examples/file-upload-download) | @loopback/example-file-upload-download | An example showing how to expose APIs to upload files |
| [example-greeter-extension](https://github.com/strongloop/loopback-next/tree/master/examples/greeter-extension) | @loopback/example-greeter-extension | An example showing how to implement the extension point/extension pattern using LoopBack 4 |
| [example-hello-world](https://github.com/strongloop/loopback-next/tree/master/examples/hello-world) | @loopback/example-hello-world | A simple hello-world application using LoopBack 4 |
| [example-lb3-application](https://github.com/strongloop/loopback-next/tree/master/examples/lb3-application) | @loopback/example-lb3-application | An example LoopBack 3 application mounted in a LoopBack 4 project. |
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
75 changes: 75 additions & 0 deletions examples/file-upload-download/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# @loopback/example-file-upload-download

An example application to demonstrate file uploads and downloads for LoopBack 4

## Summary

This application exposes `/file-upload` endpoint that accepts
`multipart/form-data` based file uploads. The uploaded files can be listed using
`/file-download` and individual files can be downloaded using
`/file-download/<filename>`.

## Key artifacts

- [FileUploadController](src/controllers/file-upload.controller.ts)

- Expose `/file-upload` endpoint to allow file uploads

- [FileUploadService - an Express middleware from multer](src/services/file-upload.service.ts)

- A service provider that returns a configured `multer` request handler

The file upload is configured with `multer` options in
[src/application.ts](src/application.ts) as follows:

```ts
// Configure file upload with multer options
const multerOptions: multer.Options = {
storage: multer.diskStorage({
// Upload files to `.sandbox`
destination: path.join(__dirname, '../.sandbox'),
// Use the original file name as is
filename: (req, file, cb) => {
cb(null, file.originalname);
},
}),
};
this.configure(FILE_UPLOAD_SERVICE).to(multerOptions);
```

- [FileDownloadController](src/controllers/file-download.controller.ts)

- Expose `/file-download` endpoint to list uploaded files
- Expose `/file-download/{filename}` endpoint to download a file

## Use

Start the app:

```sh
npm start
```

The application will start on port `3000`. Open http://localhost:3000 in your
browser. You can try to upload a few files using the web UI or API explorer.

The uploaded files will be stored in `.sandbox` folder under the application
root directory.

## Contributions

- [Guidelines](https://github.com/strongloop/loopback-next/blob/master/docs/CONTRIBUTING.md)
- [Join the team](https://github.com/strongloop/loopback-next/issues/110)

## Tests

Run `npm test` from the root folder.

## Contributors

See
[all contributors](https://github.com/strongloop/loopback-next/graphs/contributors).

## License

MIT
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@loopback/example-file-upload",
"name": "@loopback/example-file-upload-download",
"version": "1.0.0",
"description": "File upload examples for @loopback/rest",
"description": "Example application for file upload/download with LoopBack 4",
"main": "index.js",
"engines": {
"node": ">=10"
Expand All @@ -11,8 +11,8 @@
"acceptance": "lb-mocha \"dist/__tests__/acceptance/**/*.js\"",
"build": "lb-tsc",
"build:watch": "lb-tsc --watch",
"clean": "lb-clean *example-file-upload*.tgz dist *.tsbuildinfo package",
"verify": "npm pack && tar xf *example-file-upload*.tgz && tree package && npm run clean",
"clean": "lb-clean *example-file-upload-download*.tgz dist *.tsbuildinfo package",
"verify": "npm pack && tar xf *example-file-upload-download*.tgz && tree package && npm run clean",
"lint": "npm run prettier:check && npm run eslint",
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"",
Expand All @@ -29,7 +29,7 @@
"repository": {
"type": "git",
"url": "https://github.com/strongloop/loopback-next.git",
"directory": "examples/file-upload"
"directory": "examples/file-upload-download"
},
"publishConfig": {
"access": "public"
Expand Down
113 changes: 113 additions & 0 deletions examples/file-upload-download/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>File upload and download</title>

<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="shortcut icon"
type="image/x-icon"
href="https://loopback.io/favicon.ico"
/>

<style>
h3 {
margin-left: 25px;
text-align: center;
}

a,
a:visited {
color: #3f5dff;
}

h3 a {
margin-left: 10px;
}

a:hover,
a:focus,
a:active {
color: #001956;
}

.power {
position: absolute;
bottom: 25px;
left: 50%;
transform: translateX(-50%);
}

.info {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.info h1 {
text-align: center;
margin-bottom: 0;
}

.info p {
text-align: center;
margin-bottom: 3em;
margin-top: 1em;
}
</style>

<script>
async function fetchFiles() {
const res = await fetch('/file-download');
const files = JSON.parse(await res.text());
const list = files.map(
f => `<li><a href="/file-download/${f}">${f}</a></li>\n`,
);
document.getElementById('fileList').innerHTML = list.join('');
}
</script>
</head>

<body onload="fetchFiles()">
<div class="info">
<h1>File upload and download</h1>

<div id="upload">
<h3>Upload files</h3>
<form method="POST" action="/file-upload" enctype="multipart/form-data">
<label for="files">Select files:</label>
<input type="file" id="files" name="files" multiple /><br /><br />
<label for="note">Note:</label>
<input
type="text"
name="note"
id="note"
value="Note about the files"
/>
<br /><br />
<input type="submit" onclick="fetchFiles()" />
</form>
</div>

<div id="download">
<h3>Download files</h3>
<button onclick="fetchFiles()">List uploaded files</button>
<ul id="fileList"></ul>
</div>

<h3>OpenAPI spec: <a href="/openapi.json">/openapi.json</a></h3>
<h3>API Explorer: <a href="/explorer">/explorer</a></h3>
</div>

<footer class="power">
<a href="https://loopback.io" target="_blank">
<img
src="https://loopback.io/images/branding/powered-by-loopback/blue/powered-by-loopback-sm.png"
/>
</a>
</footer>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {inject} from '@loopback/context';
import {get, param, Response, RestBindings} from '@loopback/rest';
import fs from 'fs';
import path from 'path';
import {promisify} from 'util';

const readdir = promisify(fs.readdir);

/**
* A controller to handle file downloads using multipart/form-data media type
*/
export class FileDownloadController {
@get('/file-download', {
responses: {
200: {
content: {
'application/json': {
schema: {
type: 'array',
items: {
type: 'string',
},
},
},
},
description: 'A list of files',
},
},
})
async listFiles(@inject(RestBindings.Http.RESPONSE) response: Response) {
const sandbox = path.join(__dirname, '../../.sandbox');
const files = await readdir(sandbox);
return files;
}

@get('/file-download/{filename}')
async downloadFile(
@param.path.string('filename') fileName: string,
@inject(RestBindings.Http.RESPONSE) response: Response,
) {
const file = path.join(__dirname, '../../.sandbox', fileName);
response.download(file, fileName);
return response;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-file-upload
// Node module: @loopback/example-file-upload-download
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

Expand Down
File renamed without changes.
Loading

0 comments on commit 12afd6b

Please sign in to comment.