In my last blog post, I discussed how to create a backend that supports image transfers using Base64 encoding, as well as the front-end code for the iOS client. In this article, I’ll be documenting an alternative (and arguably, better) approach to handling RESTful image transfer.

Base64 encoding images was traditionally used in websites as an alternative to static file loading, helping to save an additional HTTP call to retrieve an image. In REST-land, sending base64 through API requests is useful if you want to adopt a purely JSON-driven API. That being said, encoding images in base64 format increases the size by ~33%, making it an inefficient method to transmit images between client and server.

So, what’s the better alternative?

1 Multipart Form Data

Multipart form data as part of a POST request is exactly what it sounds like — it is the standard method by which clients transmit form-based data to the server. It’s suitable for our purpose as this method of data transmission supports file uploads.

Sending images through a multipart form is sufficient if we’re uploading one image at a time (or at most, a few images). For videos and batched image uploads, it would be more feasible to use some sort of streaming upload.

2 Basic Python REST Server

As in my last blog post, a basic backend server can be created in Python with flask together with flask-restful. With flask-restful, endpoints are defined as Python classes. Again, we create a post function to indicate that this endpoint supports HTTP POST operations.

In our RequestParser, we used to define the following expected argument: parser.add_argument("image", type=str, location='json'). Now, we’re sending data over a form upload, so we change the expected argument to: parser.add_argument("image", type=werkzeug.datastructures.FileStorage, location='files')

from flask_restful import Resource, reqparse
	import werkzeug

	class ProcessImageEndpoint(Resource):

	    def __init__(self):
	      	# Create a request parser
	        parser = reqparse.RequestParser()
	        parser.add_argument("image", type=werkzeug.datastructures.FileStorage, location='files')
	        # Sending more info in the form? simply add additional arguments, with the location being 'form'
	        # parser.add_argument("other_arg", type=str, location='form')
	        self.req_parser = parser

	    # This method is called when we send a POST request to this endpoint
	    def post(self):
	      	# The image is retrieved as a file
	       	image_file = self.req_parser.parse_args(strict=True).get("image", None)
	        if image_file:
	          # Get the byte content using `.read()`
	          image = image_file.read()
	          # Now do something with the image...
	        	return "Yay, you sent an image!"
	        else:
	       		return "No image sent :("

This is not too different at all from the base64 case. By implementing image transfer this way, we save a lot of bandwidth in the long run. We also reduce the time spent in transit — translating to shorter wait times for the end user.

3 Sending Images with Swift (iOS)

As in the last blog post, recall that when you take an image using the Swift AVFoundation library, you get an output of type AVCapturePhoto. We can easily retrieve a data representation of the photo using .fileDataRepresentation().

Again, we leverage the Alamofire framework, which easily facilitates multipart form POST requests. We can then create a service with a makeRequest function:

// Function to make a request to our server - we can retrieve the image data using .fileDataRepresentation() on an AVCapturePhoto
	func makeRequest(image: Data) {
	  let endpointUrl: String = "Server URL"

	  // Use Alamofire to make a POST request
	  AF.upload(
	  	multipartFormData: { formData in 
	  		formData.append(image, withName: "someName", fileName: "someName.jpg", mimeType: "image/jpg")
	  	},
	  	to: endpointUrl
	  )
	  .response { response in
	    // Do something with the response
	    // Or - create a struct called ResponseDTO, also conforming to Codable
	    // And use `responseDecodable`
	  }
	}

If you have other arguments you would like to send, you can either attach them to the request as headers, add it to the URL as a parameter, or just add an additional entry to formData.

#python #mobile-app-development #ios #programming #software-development

Better RESTful Image Transfer: Multipart Uploads with Python Flask & Swift
60.55 GEEK