Overview
File upload is a very common feature for most applications, so learning how to upload files to a server through the API is very important. In this blog, you will learn everything from uploading files to validation.
Upload a File
You can upload files on the server in two formats one is Multipart and the other is Byte Array. Let’s take a closer look at how to handle both formats.
- Multipart Data
In this format, all information including files and other fields should be in multipart format. Use call.receiveMultipart() to retrieve all the parts from a request, a part containing a file is of type PartData.FileItem and other fields are of PartData.FormItem. See the code snippet below to understand the implementation of multipart form data.
val multipartData = call.receiveMultipart() multipartData.forEachPart { part -> when (part) { is PartData.FileItem -> { val fileBytes = part.streamProvider().readBytes() val mFile = File("<UPLOAD_FOLDER>/<FILE_NAME_WITH_EXTENSION>"); mFile.writeBytes(fileBytes) } } }
- Binary / Byte Array
When a client sends a request body in binary format, the server receives data in the array. Use call.receive<ByteArray>() to retrieve the byte array and you can directly write that array in the file. Certainly, that sounds much easier than a multipart. Use this format only when there is only a file to send with the request and no other data.
val binaryData = call.receive<ByteArray>() File("uploads/BinaryFile.png").writeBytes(binaryData) call.respond(HttpStatusCode.OK,"File Uploaded ✅")
File Location
Construct a File Object to create a file at the desired location on your server. Also, make sure you have created a folder before storing any file in it. There are multiple places where you can save your files, the most common are:
- In server where you deploy your backend
val mFile = File("<UPLOAD_FOLDER>/<FILE_NAME_WITH_EXTENSION>"); mFile.writeBytes(fileBytes)
- Temporary folder on the server
withContext(Dispatchers.IO) { File.createTempFile("img", ".png", null) //img=prefix, .png=suffix, null=directory }.writeBytes(part.streamProvider().readBytes())
Name a File
Depending on your needs you can either use the name of the uploaded file or use a random file name. part.originalFileName returns the file name of the original uploaded file. You can generate a random name using UUID.randomUUID() method, it only generates random numbers, you have to add the suffix .png or .jpg or whatever it should be to your file.
Image Resize and Compress
Thumbnailator is a popular and easy-to-use library that helps developers resize and compress images. All you have to do is provide the library with the file, size, quality and destination and it will do the rest. The following code snippet explains everything:
Thumbnails .of(mFile) .size(200, 200) .outputQuality(0.5) .toFile("uploads/${mFile.nameWithoutExtension}_thumbnail.png")
Validations
As a developer, you need to make sure that the uploaded file is valid and meets the criteria set for the image or file. There are two common types of file validation
- File Size
If you do not restrict users to upload files within certain size limits and the user uploads large files, you may face the problem of lack of storage. This can increase maintenance costs. In addition, it extends the file download time, and in the case of an image, it increases the image loading time. You can get the file size from the ConentLength of the request header. For the file type, you can use the contentType property of the multipart data. Check out the code snippet below to learn about the implementation of the above beliefs. - File Type
This validation prevents your users from uploading the wrong file to the server. You can use the content type of the multipart data to check the file type.
File Validation Example
// File Size Validation val contentLengthBytes = call.request.header(HttpHeaders.ContentLength)?.toDouble() ?: 0.0 val contentLengthMB = contentLengthBytes / 1024 / 1024 if(contentLengthMB > 1.0){ call.respond(HttpStatusCode.BadRequest, "File is too large, maximum allowed size is 1 MB ❌") } // File Type Validation val fileType = part.contentType?.contentSubtype val ACCEPTED_IMAGE_TYPES = listOf("PNG", "JPG", "JPEG") if (fileType?.uppercase() !in ACCEPTED_IMAGE_TYPES) { call.respond(HttpStatusCode.BadRequest, "Invalid file type ❌") } // Respond with valid file call.respond("Valid File ✅")
TL;DR
In most cases, the files are uploaded in multipart format, use call.receiveMultipart () to get the multipart request and then you can read the bytes and save it to the file. Use image compression libraries such as Thumbnailator to reduce image size and load faster at the client end. You can save your file to a local server or to another storage server, such as Amazon S3. Most importantly, exclude uploading trash to your server, check for file type and file size to save your server storage.
your BusinessGet Expert Assistance