Skip to content

Commit de587e4

Browse files
cyclimseredanrd
andauthored
feat(functions): add python s3 form data upload example (#29)
* feat(functions): add python s3 form data upload example * refactor: add variable for storage class * refactor: apply suggestions from code review Co-authored-by: Reda Noureddine <58738453+redanrd@users.noreply.github.com> --------- Co-authored-by: Reda Noureddine <58738453+redanrd@users.noreply.github.com>
1 parent 706c5c6 commit de587e4

File tree

6 files changed

+137
-5
lines changed

6 files changed

+137
-5
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ Table of Contents:
3939
| **[Node MultiPart Upload to S3](functions/node-upload-file-s3-multipart/README.md)** <br/> A function to upload file from form-data to S3. | node19 | [Serverless Framework] |
4040
| **[Python ChatBot](functions/python-dependencies/README.md)** <br/> A chatbot example with ChatterBot. | python310 | [Serverless Framework] |
4141
| **[Python Dependencies](functions/python-dependencies/README.md)** <br/> Example showing how to use Python requirements with Serverless Framework. | python310 | [Serverless Framework] |
42+
| **[Python MultiPart Upload to S3](functions/python-upload-file-s3-multipart/README.md)** <br/> A function to upload file from form-data to S3. | python311 | [Python API Framework] |
4243
| **[Redis TLS](functions/redis-tls/README.md)** <br/> How to connect a function to a Scaleway Redis cluster with TLS enabled. | python310 | [Terraform] |
4344
| **[Rust MNIST](functions/rust-mnist/README.md)** <br/> A Rust function to recognize hand-written digits with a simple neural network. | rust165 | [Serverless Framework] |
4445
| **[PostgreSQL Python](functions/postgre-sql-python/README.md)** <br/> A Python function to perform a query on a PostgreSQL managed database. | python310 | [Serverless Framework] |
4546
| **[Terraform Python](functions/terraform-python-example/README.md)** <br/> A Python function deployed with Terraform. | python310 | [Terraform] |
46-
| **[PostgeSQL Node](functions/postgre-sql-node/README.md)** <br/> A Node function to connect and interact with PostgreSQL database. | node18 | [Serverless Framework] |
47+
| **[PostgeSQL Node](functions/postgre-sql-node/README.md)** <br/> A Node function to connect and interact with PostgreSQL database. | node18 | [Serverless Framework] |
4748

4849
### 📦 Containers
4950

@@ -62,6 +63,7 @@ Table of Contents:
6263

6364
[Serverless Framework]: https://github.com/scaleway/serverless-scaleway-functions
6465
[Terraform]: https://registry.terraform.io/providers/scaleway/scaleway/latest/docs
66+
[Python API Framework]: https://github.com/scaleway/serverless-api-project
6567

6668
## Contributing
6769

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Node function to read files and upload to S3
22

3-
This function does the following steps :
3+
This function does the following steps:
44

55
* Read a file from an HTTP request
66
* Send the file to an S3 bucket
77

88
## Requirements
99

10-
If you want to enable S3 upload, ensure to create a bucket and have the following secrets variables available in your environment:
10+
Ensure to create a bucket and have the following secrets variables available in your environment:
1111

1212
```env
1313
S3_REGION = # Default: fr-par
@@ -16,8 +16,12 @@ SCW_SECRET_KEY =
1616
BUCKET_NAME =
1717
```
1818

19+
## Running
20+
1921
To call the function (replace `README.md` with the file you want to upload):
2022

21-
```sh
22-
curl -X POST -F "data=@README.md" -F "another=@package.json" http://localhost:8080
23+
```console
24+
serverless deploy
25+
26+
curl -X POST -F "data=@README.md" -F "another=@package.json"
2327
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Python function to upload files to S3
2+
3+
This function does the following steps:
4+
5+
* Read a file from an HTTP request form
6+
* Send the file to long-term storage with Glacier for S3
7+
8+
## Requirements
9+
10+
This example uses the [Python API Framework](https://github.com/scaleway/serverless-api-project) to deploy the function.
11+
12+
If needed, create a bucket and provide the following variables in your environment:
13+
14+
```env
15+
export SCW_ACCESS_KEY =
16+
export SCW_SECRET_KEY =
17+
export BUCKET_NAME =
18+
```
19+
20+
## Running
21+
22+
### Running locally
23+
24+
This examples uses [Serverless Functions Python Framework](https://github.com/scaleway/serverless-functions-python) and can be executed locally:
25+
26+
```console
27+
pip install -r requirements-dev.txt
28+
29+
python app.py
30+
```
31+
32+
The upload endpoint allows you to upload files to Glacier via the `file` form-data key:
33+
34+
```console
35+
echo -e 'Hello world!\n My contents will be stored in a bunker!' > myfile.dat
36+
curl -F file=@myfile.dat localhost:8080
37+
```
38+
39+
### Deploying with the API Framework
40+
41+
Deployment can be done with `scw_serverless`:
42+
43+
```console
44+
scw_serverless deploy app.py
45+
```
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import TYPE_CHECKING
2+
import logging
3+
import os
4+
5+
from scw_serverless import Serverless
6+
if TYPE_CHECKING:
7+
from scaleway_functions_python.framework.v1.hints import Context, Event, Response
8+
9+
import boto3
10+
from streaming_form_data import StreamingFormDataParser
11+
from streaming_form_data.targets import ValueTarget
12+
13+
SCW_ACCESS_KEY = os.environ["SCW_ACCESS_KEY"]
14+
SCW_SECRET_KEY = os.environ["SCW_SECRET_KEY"]
15+
BUCKET_NAME = os.environ["BUCKET_NAME"]
16+
17+
# Files will be uploaded to cold storage
18+
# See: https://www.scaleway.com/en/glacier-cold-storage/
19+
STORAGE_CLASS = "GLACIER"
20+
21+
app = Serverless(
22+
"s3-utilities",
23+
secret={
24+
"SCW_ACCESS_KEY": SCW_ACCESS_KEY,
25+
"SCW_SECRET_KEY": SCW_SECRET_KEY,
26+
},
27+
env={
28+
"BUCKET_NAME": BUCKET_NAME,
29+
"PYTHONUNBUFFERED": "1",
30+
},
31+
)
32+
33+
s3 = boto3.resource(
34+
"s3",
35+
region_name="fr-par",
36+
use_ssl=True,
37+
endpoint_url="https://s3.fr-par.scw.cloud",
38+
aws_access_key_id=SCW_ACCESS_KEY,
39+
aws_secret_access_key=SCW_SECRET_KEY,
40+
)
41+
42+
bucket = s3.Bucket(BUCKET_NAME)
43+
44+
logging.basicConfig(level=logging.INFO)
45+
46+
47+
@app.func()
48+
def upload(event: "Event", _context: "Context") -> "Response":
49+
"""Upload form data to S3 Glacier."""
50+
51+
headers = event["headers"]
52+
parser = StreamingFormDataParser(headers=headers)
53+
54+
# Defined a target to handle files uploaded with the "file" key
55+
target = ValueTarget()
56+
parser.register("file", target)
57+
58+
body: str = event["body"]
59+
parser.data_received(body.encode("utf-8"))
60+
61+
if not (len(target.value) > 0 and target.multipart_filename):
62+
return {"statusCode": 400}
63+
64+
name = target.multipart_filename
65+
66+
logging.info("Uploading file %s to Glacier on %s", name, bucket.name)
67+
bucket.put_object(Key=name, Body=target.value, StorageClass=STORAGE_CLASS)
68+
69+
return {"statusCode": 200, "body": f"Successfully uploaded {name} to bucket!"}
70+
71+
72+
if __name__ == "__main__":
73+
from scaleway_functions_python import local
74+
75+
local.serve_handler(upload)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-r requirements.txt
2+
3+
scaleway-functions-python~=0.1.0
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
scw-serverless~=0.0.4
2+
boto3~=1.26
3+
streaming_form_data~=1.11.0

0 commit comments

Comments
 (0)