Skip to main content

Upload

Send files to the API and save them to a folder or to the database.

Introduction​

When we need to send files to REST API services, we have several ways to implement this in Netuno.

Usually, files are sent in the HTTP request body structured in multipart format; this is the most common procedure in the frontend, for example when we fill out a form and there is a field to choose a file.

Alternatively, we can send the file bytes encoded in Base64 as a value in a JSON object, when we want a 100% JSON REST API.

Files​

Through _req.file, we can pass the field name and thus get the file object that was sent; see how:

const myFile = _req.file("file");

That is, _req has all the data received in the HTTP request body.

And through the file method, it allows obtaining the object representing the file that was sent.

The obtained value may be null if there is no file.

Images​

When we receive images, there is often a need to resize them, to ensure uniformity and to reduce the space occupied by the images.

const myFile = _req.getFile("image");
if (myFile != null
&& (myFile.isExtension("jpeg")
|| myFile.isExtension("jpg")
|| myFile.isExtension("png")
)) {
const myImage = _image.init(myFile)
.resize(200, 200)
.file(myFile.baseName() +".jpg", "jpeg");
_header.contentTypeJPG();
_out.copy(myImage);
}

With the polyglot low-code programming resource Image, we can manipulate images, crop, resize, create new ones, etc.

Base64 in JSON​

To upload a file in the JSON sent to the service, we can define a property with the value:

data:image/png;base64,...

Where ... is the sequence of file bytes encoded in Base64.

The file type follows data, for example:

  • PNG: image/png.
  • JPG: image/jpeg
  • TXT: text/plain
  • PDF: application/pdf
  • XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

See the example of a small PNG image file being sent in the JSON to a service:

{
"file": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMyIKICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjMiCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMyIKICAgdGlmZjpJbWFnZVdpZHRoPSIzIgogICB0aWZmOlJlc29sdXRpb25Vbml0PSIyIgogICB0aWZmOlhSZXNvbHV0aW9uPSI3Mi8xIgogICB0aWZmOllSZXNvbHV0aW9uPSI3Mi8xIgogICB4bXA6TWV0YWRhdGFEYXRlPSIyMDI1LTEyLTI5VDE5OjEyOjI3WiIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjUtMTItMjlUMTk6MTI6MjdaIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0icHJvZHVjZWQiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFmZmluaXR5IERlc2lnbmVyIDEuMTAuNSIKICAgICAgeG1wTU06d2hlbj0iMjAyNS0wMi0yM1QyMjoxNDozM1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0icHJvZHVjZWQiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFmZmluaXR5IDMuMC4xIgogICAgICBzdEV2dDp3aGVuPSIyMDI1LTEyLTI5VDE5OjEyOjI3WiIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+UGhcxwAAAYFpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZG7SwNBEIe/JEqCRhS0sFAIEq3iG4I2ggmigkiIEXw1yZmHkMdxlyDBVrANKIg2vgr9C7QVrAVBUQSx1lbRRuWc84QEMbPMzre/3Rl2Z8EeSSsZvaYPMtm8Fh4PeObmFzzOZ9y004yL3qiiq6Oh0BRV7f0Wmxmvu81a1c/9a/XLcV0Bm0t4RFG1vPCE8NRqXjV5S7hFSUWXhU+EfZpcUPjG1GMWP5mctPjTZC0SDoK9SdiTrOBYBSspLSMsL8ebSReU3/uYL3HHs7MzEjvE29AJM04AD5OMEcRPP8My++lmgB5ZUSW/7yd/mpzkKjKrFNFYIUmKPD5RC1I9LjEhelxGmqLZ/7991RODA1Z1dwBqHw3jtROcm/BVMoyPA8P4OgTHA5xny/m5fRh6E71U1rx70LgOpxdlLbYNZxvQeq9GteiP5BC3JxLwcgwN89B8BXWLVs9+9zm6g8iafNUl7OxCl5xvXPoGe8Rn71E5kqUAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAySURBVAiZAScA2P8BJYrEogr//l3h5+qlBP4HCF3n/AAA/wP7WwLu7PKk+eHWAPzWyKSmfRbbIj23ZQAAAABJRU5ErkJggg=="
}

We can get the sent file as follows:

const myFile = _req.getFile("file");
if (myFile != null) {
_out.json(
_val.map()
.set("result", true)
.set("name", myFile.name())
.set("sizeBytes", myFile.size())
.set("sizeKB", myFile.sizeKB())
.set("sizeMB", myFile.sizeMB())
.set("sizeGB", myFile.sizeGB())
);
} else {
_out.json(
_val.map()
.set("result", false)
);
}

Example of the output generated by the service above:

{
"name": "file.png",
"result": true,
"size": 1849,
"sizeKB": 1
}

Object​

If the file is in an internal JSON structure inside the subobject:

{
"person": {
"file": "data:image/png;base64,..."
}
}

It can be obtained as follows:

const myFile = _req.getValues("person").getFile("avatar");

Array​

If the file is in an internal JSON structure inside the array:

{
"files": [
"data:image/png;base64,..."
]
}

It can be obtained as follows:

const myFile = _req.getValues("files").getFile(0);

OpenAPI​

Netuno supports integration with OpenAPI and uses the OpenAPI definition to validate the HTTP request.

More about OpenAPI in Netuno.

With the file type, Netuno validates whether the value starts with the file format in JSON, for example:

  • data:image/png;base64,...

To validate that a file is being received in a JSON object property:

post.in.json
{
"summary": "File Upload",
"description": "Service that receives a file in the JSON object.",
"type": "object",
"properties": {
"file": {
"type": "file"
}
},
"required": [
"file"
]
}

To validate that a list of files is being received in a JSON array:

post.in.json
{
"summary": "File Uploads",
"description": "Service that receives a list of files in a JSON array.",
"properties": {
"files": {
"type": "array",
"items": {
"type": "file"
}
}
},
"required": [
"files"
]
}

Saving the File​

Usually, when we receive a file in the service, we need to save it, for example:

  • Database: File related to a record, such as a user.
  • Storage: Internal application folder used to manage files in general.
  • Folder: Place the file in any folder on the system.

Database​

In form field management, Netuno supports the image and file types, as follows:

  • Image: Image files in general, JPG and PNG.
  • File: Any type of file.

Imagine that there is an Image field in the Product form; we can save the file sent via upload as follows:

Example using DB Form:​

Note the use of _db.form:

services/product/image/post.js
const productUid = _req.getString("uid");
const productImage = _req.getFile("image");

if (productImage == null) {
_header.status(400);
_out.json(
_val.map()
.set("result", false)
.set("error", "image-required")
);
_exec.stop();
}

const dbResult = _db.form("product")
.set("image", productImage)
.where(_db.where("uid").equals(productUid))
.update();

if (dbResult.getInt("product") == 1) {
_out.json(
_val.map()
.set("result", true)
);
} else {
_header.status(404);
_out.json(
_val.map()
.set("result", false)
.set("error", "product-uid-does-not-exist")
);
}

Example using classic update:​

Note the use of _db.update:

services/product/image/post.js
const productUid = _req.getString("uid");
const productImage = _req.getFile("image");

if (productImage == null) {
_header.status(400);
_out.json(
_val.map()
.set("result", false)
.set("error", "image-required")
);
_exec.stop();
}

const dbResult = _db.update(
"product",
productUid,
_val.map()
.set("image", productImage)
);

if (dbResult == 1) {
_out.json(
_val.map()
.set("result", true)
);
} else {
_header.status(404);
_out.json(
_val.map()
.set("result", false)
.set("error", "product-uid-does-not-exist")
);
}

Downloading the File from the Database​

To display the stored file, we must create a service that provides the file bytes.

For files saved in the database, as in the example above, the file itself is placed in the application storage, and only the final file name is stored in the database.

The application storage is a folder intended to store and organize generic file structures.

Therefore, in the example above, the database files of the Product form and the Image field are stored in:

  • storage/database/product/image/*

Netuno automatically manages the stored file name to avoid duplicate file names.

To get the file, a service can be created that displays the product image file, for example:

services/product/image/get.js
const productUid = _req.getString("uid");

// DB Form:
const dbProduct = _db.form("product")
.where(_db.where("uid").equals(productUid))
.first();
// Alternative with Classic DB:
// const dbProduct = _db.get("product", productUid)

if (dbProduct == null) {
_header.status(404);
_out.json(
_val.map()
.set("result", false)
.set("error", "product-not-found")
);
_exec.stop();
}

const dbProductImageName = dbProduct.getString("image");

const storageProductImageFile = _storage.database(
"product",
"image",
dbProductImageName
);

if (storageProductImageFile.extension() == "jpg" || storageProductImageFile.extension() == "jpeg") {
_header.contentTypeJPG();
} else {
_header.contentTypePNG();
}

_header.noCache();

_out.copy(storageProductImageFile.inputStream());

Note that the _storage resource (Storage) is used to get the file:

_storage.database(
"product", // Form/table name.
"image", // Field/column name.
dbProductImageName // File name.
);

Only the file name is stored in the database, and through _storage.database we can get the file itself.

Storage​

We can save a file directly in the application storage.

To avoid mixing with database files, we should use the filesystem folder, which is intended to store generic files that are not associated with the database.

To receive the file, this example demonstrates how a file sent in the POST service is stored directly in storage:

services/file/post.js
const file = _req.getFile("file");
_storage.filesystem("server", "upload").ensurePath();
file.save(_storage.filesystem("server", "upload", file.name()));

The file is stored in the application storage folder, in:

  • storage/filesystem/server/upload/*

The Storage resource is used to get the folder and file path:

_storage.filesystem(
"server", // Name of the subfolder in the filesystem.
"upload", // Name or path of the internal subfolder.
file.name() // Final file name.
);

See more in the Storage documentation.

To download the file, this example demonstrates how the file bytes can be obtained through the GET service:

services/file/get.js
const fileName = _req.getString("file");

const file = _storage.filesystem(
"server",
"upload",
fileName
).file();

if (file.exists() == false) {
_header.status(404);
_out.json(
_val.map()
.set("result", false)
.set("error", "file-does-not-exist")
);
_exec.stop();
}

_header.downloadFile(file.name());
_out.copy(file.input());

Folder​

We can save a file directly in any folder on the computer.

There are two resources that are very useful in these cases:

  • _app - We can get any folder or file in the application.
  • _os - We can get any folder or file on the computer.

See the documentation for the respective resources App and OS; both resources have the folder and file methods, which allow indicating the folder or file path, respectively.

To receive the file, this example demonstrates how a file sent in the POST service is stored directly in a folder in the application:

services/file/post.js
const file = _req.getFile("file");
const folder = _app.folder("temp");
if (folder.exists() == false) {
folder.mkdir();
}
file.save(_app.file(`temp/${file.name()}`));

The code above will create the temp folder inside the application and save the received file inside this folder.

To download the file, this example demonstrates how the file bytes can be obtained through the GET service:

services/file/get.js
const fileName = _req.getString("file");
const file = _app.file(`temp/${fileName}`);

if (file.exists() == false) {
_header.status(404);
_out.json(
_val.map()
.set("result", false)
.set("error", "file-does-not-exist")
);
_exec.stop();
}

_header.downloadFile(file.name());
_out.copy(file.input());

Conclusion​

As can be seen, we can integrate file uploads into REST API services, including integration with OpenAPI.

We can save the file in the database, inside the application storage folder, or in any folder on the computer.

See the documentation for the resources used in this tutorial that allow manipulating files:

In the database, we can save images in fields of type image, and we can save any type of file in fields of type file; how we save or get files is the same in any case, whether image or generic file.