How to Build an Api for Uploading the Picture

Welcome to a new, hopefully heady tutorial! In a previous mail I showed to you the process of creating a custom class that manages spider web requests and RESTful APIs. Today, we volition keep building on it, as I would similar to focus on a specific apply example: How to upload files to a server!

Uploading files might not be i of the well-nigh common things when dealing with web services. However, it tin exist proved to be a tedious task to perform when information technology's time to send files to a server. In the implementation steps that follow we will try to interruption things downwards and shed calorie-free to the cardinal points and the details of the uploading procedure. Before we get there though, it's necessary to have a quick word about some footing knowledge that nosotros all should take on this topic.

A Quick Intro To "Multipart/form-information" Content Type

Before nosotros start doing actual piece of work, it'south necessary some important things to be mentioned first. Allow me offset by saying that in order to upload files to a server, multipart/form-information is the content type that should be specified in the spider web request. This content type allows to ship files or large amounts of data in combination with other usual data that should be posted. "Multipart/form-information" content blazon tells to HTTP request that posted data should exist broken into parts, as if they were to be posted by a spider web grade that expects from users to fill up in various fields and select files that should be submitted to a server.

Since posted data is broken into parts, it'south necessary for the server to know where a part starts and where it ends. For that purpose, a special and unique string is provided along with the content blazon, chosen boundary. That string should not occur in the actual data, so it must be as much unique equally possible. It e'er starts with two dashes ("–"), with an arbitrary combination of other alphanumeric characters coming afterwards. Unremarkably, boundaries start with multiple dashes, and then they have an alphanumeric suffix (e.g. —————–abc123).

Each role of a multipart body necessarily starts with a Content-Disposition header, with the class-data value coming in pair with information technology. An attribute called "name" should besides exist provided in the header, as information technology specifies the proper name of the part. Observe that names don't need to be unique, and sometimes server sets the rules that apply to the "name" aspect. These two cardinal-value pairs are enough when adding unmarried data (meaning no files) to the asking'south HTTP trunk. When appending files information, the filename should be besides included in the "Content-Disposition" header with the original name of the file, besides as the content blazon (MIME blazon) of each file that is about to be uploaded.

The following is a fake instance of a HTTP request body that uses the "multipart/form-data" content blazon:

Notice how everything mentioned in the previous paragraphs is used. At showtime, the "multipart/class-data" content blazon is specified forth with the boundary cord that separates the data parts. See how purlieus indicates the beginning of each part and also meet how semicolon (";") separates attributes in headers. Line breaks are also important when building a HTTP trunk such the above i. In single fields, an empty line exists between the "Content-Disposition" header and the actual field value, while the purlieus of the side by side office comes right after in the next line. In file parts, the "filename" attribute contains the proper name of the file, while an additional empty line exists between the file contents and the side by side boundary. The torso catastrophe is highlighted past the purlieus, plus ii more than dashes equally a suffix to it.

I am encouraging you to take a await at the W3C HTML Specification and read more about encoding content types and the "multipart/course-information" especially. You don't take to terminate at that place of form; a full general search on the web will return lots of resources to read about this topic.

About The Demo App

And then, as I said in the get-go of this mail service, we are going to keep building on the custom class we created in the previous tutorial, called RestManager. To go started, please download a starter packet which contains a Xcode projection with that course and one more directory with a demo server implementation (run into next part). In Xcode project yous will observe iii files that we'll utilize to test file uploading after we finish all implementation steps:

  • A text file named SampleText.txt with "lorem ipsum" data generated here.
  • A PDF file named SamplePDF.pdf taken from File Examples.
  • An epitome file named SampleImage.jpg downloaded from Pexels (Photograph by Oleg Magni from Pexels).

No UI will exist in our app, and the results of our last tests will be printed in Xcode panel and in Terminal. Any input values will exist hard-coded. Therefore, we'll entirely focus on the file uploading characteristic that nosotros'll add together to the RestManager class. Plainly, you are free to create any UI you desire if y'all want to create a more than dynamic demo application.

About The Server

After we finish implementing all the new lawmaking nosotros'll meet in the post-obit parts, nosotros'll need to test if file uploading is really working. For that purpose, a uncomplicated server implemented in Node.js is included in the starter parcel that y'all downloaded; you will notice information technology in the Server subdirectory. Yous can keep it in the location that currently is, or re-create it anywhere else yous want in your disk.

In gild to run the server, yous must have Node.js installed on your computer. If you don't, delight check here or here on how to do that. Open Terminal and type the following control:

There is a space character after the cd command. And then switch to Finder, and drag and drib the Server directory to terminal and printing the Return key:

By doing and so, you don't have to type the path to the server directory; information technology'south automatically appended to the command in concluding.

To verify that you are successfully in the server directory, just blazon:

This command will evidence the electric current directory contents, and if you lot see something like to the side by side one, so you're merely fine:

To get-go the server merely type:

You should see the message:

Server started successfully on port 3000!

The server is now running at address http://localhost:3000. Y'all can also verify that if you paste that address in a new tab in your browser. You'll run into a message coming from the server.

Annotation: If y'all are already running another server at port 3000, edit the index.js file and set a custom port number to the port variable. Then restart the server with the node index.js command.

Requests made to "http" addresses are non allowed by default in iOS as they are considered insecure. However, for the sake of the tutorial, localhost has been whitelisted in the Info.plist file of the starter projection then you will encounter no trouble in testing the app later.

Representing Files

The first thing we demand to take care of is how files are going to be represented in the RestManager class. For any file that is about to be uploaded, we need to have the post-obit data available at the time of the HTTP body grooming:

  • The bodily file contents.
  • The original file name. Call back that the filename aspect must exist in the "Content-Disposition" header of each part that represents a file.
  • The office's name for the name aspect in the "Content-Disposition" header.
  • The content type (MIME type) of the file.

Plainly, all that data could exist stored in a dictionary, just that wouldn't exist the best arroyo in Swift. To practice it better, permit'due south create a struct which we'll call FileInfo. Open the RestManager.swift file in the starter Xcode project, and go to the finish of it. You will find the post-obit empty extension:

This is where we'll add almost all new code regarding the file uploading feature. Within this extension, add the post-obit construction:

The four properties will go on the data described before. As you will see later, if any of the above properties is nil the file won't exist added to the HTTP body for submission to the server.

Nosotros can make the initialization of a FileInfo object more friendly if we add together the following custom initializer:

With this initializer, it won't exist necessary to provide the actual file contents when creating a FileInfo object. Specifying the URL of the file volition be plenty. File contents volition be read in the above initializer.

Creating The Purlieus

Having a solution on our hands virtually how to stand for files, permit's create a method which volition be responsible of creating the boundary string. Call up that a boundary must be unique and definitely non an ordinary string that could exist potentially institute in the actual data that will be uploaded. As I said in the beginning of the post, even though boundaries start with two dashes ("–"), they usually have several more dashes following and a random alphanumeric string at the end. That'south not mandatory, but information technology'southward the logic we volition follow here.

Right after the FileInfo struct, define the following private method:

I will show y'all two different ways to generate the random boundary string.

Using A UUID String

The fastest fashion to get a random string is to generate a UUID value:

The in a higher place will generate something like to this:

Let's get rid of the dashes in that string, and let's convert all letters to lowercase:

The original UUID will now look like this:

Let's construct the boundary string. It will be a chain of twenty dashes at the start and the transformed UUID value:

If you like exaggerating, add together the current timestamp to the end as well:

A purlieus string created with the above volition look like:

Well, that looks quite unique and random, no?

Here's the implementation of the unabridged method:

Using Random Characters

As an alternative to the above we tin create a machinery which will pick random characters from a collection of available characters, and using them to form a string which volition exist appended to the boundary string. The collection of available characters will exist parted by all messages ranging from upper cased "A" to "Z", lower cased "a" to "z", and all digits from "0" to "9".

Nosotros won't really need to hard-code anything, as nosotros tin programmatically construct everything. Nosotros will be based on the ASCII tabular array for that.

Nosotros'll start by specifying the range of the lower cased characters ("a" to "z") in the ASCII table as shown below:

The higher up is equivalent to this:

where 97 is the position of the "a" character and "122" is the position of the "z" graphic symbol in the ASCII tabular array.

Still, the 2d line of code requires from us to search for an ASCII tabular array online and and then locate the position of the characters we are interested in into the table. Okay, it'due south piece of cake, but information technology'southward definitely not the recommended manner, since we can get the values nosotros desire by using the UInt8(ascii:) initializer. And that's we practise in the first identify.

Similarly, we become the ranges of the upper cased A-Z and of the digits:

Now, permit's join all these ranges into a collection, or in other words a sequence of ranges (closed ranges more particularly) with aim to get the bodily characters afterwards:

If we print the value of the sequenceOfRanges to the console at runtime we'll get this:

Fifty-fifty though it's non obvious unless someone looks upwards for it, the above tin be easily converted into a String value:

Data struct provides several initializers for creating a data object and at that place is one among them that accepts a sequence as an argument, exactly equally nosotros practise in the Information(sequenceOfRanges) expression. From that data object, we can create the following cord which is assigned to the toString constant:

That cool! Permit'southward generate a string of 20 random characters now:

At first we initialize a string value chosen randomString. Then, we create a loop that will be executed 20 times. In information technology, nosotros pick a random grapheme from the toString string using the randomElement() method, and we generate a new String value (String(toString.randomElement()!)). This new String value is appended to the randomString.

Annotation that is safe to force unwrap the value of the randomElement() method, every bit it returns nil just in cases of empty collections. Here we know that toString won't be empty.

The following is a random value of the randomString:

Finally, we can build the boundary string:

Here is a sample of the boundary:

The createBoundary() method with the second implementation in one place:

Use the implementation you prefer the about. The second one is more "Swifty" but information technology requires a fleck of more lawmaking. At the end of the day, both approaches are going to piece of work every bit well.

An important note: I've mentioned already that the boundary string which separates the parts of a multipart body starts with two dashes ("–"). These two dashes are non included in the dashes of the boundary string nosotros generated in both approaches here. This string volition be provided as-is to the request as a request header along with the content type and server volition try to locate it after the two dashes prefix. As well, a boundary string can exist with no dashes at all; nosotros just add them to minimize the possibility to find similar string in the uploaded information. Every bit you will encounter subsequently, the two dashes prefix volition be manually appended whenever necessary.

Extending Data Structure

Our next steps involve the preparation of the HTTP trunk using whatever arbitrary data provided to the grade, as well as using the files information. Only before we get into that, nosotros will extend the Data structure and we volition create the following generic method:

The purpose of this method is to let us easily append the values of the values drove to the data object that calls it. And as yous'll run into, we'll exist interested for Cord and Data types only.

Just for clarification, we could avoid implementing this method. However, the code that we volition add to it would have to be repeated multiple times in different points in the RestManager class, and that definitely would not be a wise motion.

So, to continue go to the end of the RestManager.swift file where you will find a Information extension:

Add the new method'south definition in it:

At first, we'll declare the following two local variables:

Side by side, we'll distinguish the blazon of the given values. Permit'south start with the String type. In this case, nosotros'll make a loop to admission all values in the values parameter collection:

In each repetition nosotros will convert the string value into a Data object and we will suspend information technology to the local newData variable. If for some reason the string value cannot be converted into a Data object, we'll set the condition flag to faux and nosotros'll suspension the loop.

We will follow a quite like approach in case of Data input values. Of form, there is no need to initialize any new Data object or make a conversion of any blazon. We are appending i information value to some other:

Lastly, permit's signal that we don't care near any other type of values:

Next, we'll check the condition value. If it's truthful, then we tin can append the newData local variable to the self object (the Data object that is used to telephone call this method).

At the terminate, we should not forget to return the status as the outcome of the method:

Here's the entire implementation. We are going to put it in action starting from the next part.

Creating the HTTP Body

In the electric current implementation of RestManager there is a method named getHttpBody(). Its purpose is to fix the HTTP body with the data that will exist posted to the server. Although this method works not bad in whatsoever other instance, unfortunately it's not of much help in case of file uploading. There is the boundary string nosotros accept to take into account, likewise as the special headers and formatting required when using the "multipart/form-data" content type. To serve our new needs, we'll implement a similarly named method which will be accepting the boundary cord every bit an argument (also known as method overloading).

In the new extension of the RestManager form, right below the createBoundary method, add the post-obit:

Continue in heed that the HTTP trunk must exist a Data value, and so nosotros are initializing such a value in this method, and this is also what the method returns. In this method we'll bargain with any data that should exist posted to the server except for files. That's the data that would be normally submitted if in that location were no files to upload at the same fourth dimension, and information technology's kept in the httpBodyParameters property (as a reminder, httpBodyParameters is a holding in the RestManager course and it's of RestEntity type, a custom structure – find information technology in RestManager and read more in the previous tutorial about information technology).

httpBodyParameters has a method called allValues() and returns all information equally a dictionary (a [Cord: Cord] dictionary). We'll employ it to admission all values that should be sent to the server and append them to the trunk variable. Right after the var body = Data() line add the following:

A small stop here now equally we have to discuss what exactly we'll be appending to the body. Allow'south see once more part of the example presented in the beginning of this mail service:

In this example the information is the username and the countersign. The post-obit apply to each piece of information:

  • At first in that location is the boundary string, and right subsequently that a line suspension. In HTTP headers, a line interruption is marked with "\r\northward" (carriage return and new line character), not simply the "\n" that we are by and large used to. Programmatically, this could exist written like: "--\(purlieus)\r\n" (see the ii dashes before the boundary string).
  • Next, at that place is the "Content-Disposition" header with the proper name aspect only in it. Header is followed by a line pause 2 times. We could write this like and then: "Content-Disposition: form-data; proper noun=\"\(primal)\"\r\due north\r\north".
  • Lastly, it'south the bodily value followed by a line break. That'south easy: "\(value)\r\due north".

We volition add the code that represents each step described higher up into an array:

We will apply for first time the append(values:) custom method we implemented in the previous step in gild to convert these strings into Data objects and append them to the body variable:

And that'due south the last matter we had to do in this method. Let's run across information technology altogether at present:

We'll employ the results of this method in a while. For now, we have to add the files data to the HTTP body every bit well.

Adding Files To HTTP Body

One could say that the getHttpBody(withBoundary:) method we just implemented along with the new one we will implement here consist of the most important part of the overall piece of work we take to do in order to make file uploading possible. And that would be pretty much true, as we've built all the helper methods we demand and at present nosotros are dealing with the core functionality.

Then, continuing on building the HTTP torso, let'due south ascertain the following new method:

Permit's talk first well-nigh the parameters. The starting time one is a collection of FileInfo objects, and it contains the data for all files that are about to exist uploaded. The second parameter value is the data object that represents the HTTP body. Any changes that will exist made to that object inside this method will be reflected out of it likewise because it'southward marked with the inout keyword. The last parameter is the purlieus string, every bit we necessarily demand information technology to separate data parts.

You might be wondering why this method returns an optional array of String values. Well, in case there are files whose data cannot be added to the HTTP trunk, then we'll keep their names into an assortment, which in turn the method volition return. In normal conditions this method should return naught, meaning that data from all files was successfully appended to the HTTP trunk information.

Let'southward start adding some code, with the get-go one existence the following local variables:

status volition point whether all pieces of data for each single file in the files collection were successfully combined in one Data object, which can be and then appended to the torso inout parameter. If status is false, we'll be appending the name of the matching file to the failedFilenames array.

Let's offset a loop now:

The commencement thing we have to do is to make sure that all backdrop of each file object have actual values then we tin can keep:

Side by side, we will set the initial value of the status flag on each repetition of the loop to fake, and we'll initialize a new Data object.

At present, let'due south run across again the instance presented in the beginning of the tutorial so we understand what we have to do:

Going pace past step through the lines that describe a file role:

  • At kickoff in that location is the purlieus with the line intermission at the finish. Nosotros already know how to write that in lawmaking.
  • Next, we have the "Content-Disposition" header. The addition here (comparison to the header in the previous part) is the new filename attribute which contains the actual file name. In code such a header is written like this: "Content-Disposition: grade-data; name=\"\(name)\"; filename=\"\(filename)\"\r\due north".
  • Right after we take the content type of the file. Come across all the available MIME Media Types. In code this is similar so: "Content-Type: \(mimetype)\r\due north\r\n".

Allow's brand a suspension hither and allow'due south append all the above to an array:

Let's convert all strings in that array into Data objects and append them to the information variable:

Let's keep where we had stopped from. The next item in a file part is the actual file contents. Think that file contents are represented by the fileContents belongings in a FileInfo object, which is a Data object. So far nosotros were dealing with strings only. File contents must exist appended to the data variable likewise:

Remember that append(values:) method expects for an array of values, and then it'southward necessary to include content into the assortment'south opening and endmost brackets to a higher place.

Lastly, notice in the above instance that there is an empty line right after the file contents which should be added to the data besides:

These three conditions we wrote must exist embedded into each other. If all of them are true, so all data pieces for the electric current file were successfully added to the information object, and we'll betoken that by making the status true:

See that nosotros used the custom append(values:) custom method iii times in a row here. I promise you agree that its implementation was meaningful since we use it once more and once more.

Next, let'due south bank check the status value for each file. While nevertheless existence on the loop:

If status is true, we append the data variable to the body which represents the HTTP body. If not, then nosotros initialize the failedFilenames array in case information technology's not initialized already, and we keep the proper noun of the electric current file in it.

Ane last thing remaining, to return the failedFilenames from the method:

Our new method should now await like this:

Closing The HTTP Body

Now that we created methods which build the HTTP body past appending any post data and file data, nosotros must create one more than which volition shut the body. Remember that in "multipart/grade-data" the HTTP torso closing is marked by the purlieus string and two dashes as a suffix to it:

Every bit you can gauge, doing then doesn't require much of work as all it takes is this:

For one more than time here the body parameter is marked as inout, so the information argument will be passed past reference and the changes made to it inside this method will get visible to the caller likewise. Likewise that, notice the line breaks before and afterwards the closing cord which ensure that the closing boundary will be the only content in the line.

Information technology's really important non to forget to call this method and point the end of parts in the multipart HTTP trunk.

Uploading Files

It'southward about time to put everything together and make file uploading possible. The method we'll write hither volition exist public, so you can go and add it to the top of the class forth with other two public methods existing already. Here is its definition:

In accordance to what nosotros did to the other two existing public methods, we are going to perform all actions in this method asynchronously. Nosotros won't run annihilation on the main thread since file uploading could have pregnant amount of fourth dimension and nosotros don't want apps to bear witness frozen. In code that ways:

With userInitiated value in the quality of service parameter we requite our chore a relatively high priority in execution. Annotation that we marker self every bit weak in the closure since the RestManager instance used to perform the file uploading can potentially get zippo, and that practically ways that self is from at present on an optional. This introduces a couple of new needs as you will run into next.

The kickoff actual action nosotros have to have is to add together any URL query parameters specified in the urlQueryParameters property to the URL. This will happen by calling the addURLQueryParameters(toURL:) method which we implemented in the previous tutorial:

Next, let's call the createBoundary() method nosotros implemented today and let'south create the boundary string:

Find that since cocky is used as an optional, purlieus becomes an optional value as well, regardless of the fact that createBoundary() does not return an optional. So, in case there's no boundary string to continue, we telephone call the completion handler passing the mistake shown above and we render from the method. This custom error doesn't exist yet in the class, we'll add it in a while.

Let's get going, and in the next step let's add together the "multipart/form-information" along with the boundary string to the collection of the request headers:

To refresh your memory, requestHttpHeaders is a RestEntity holding which keeps all HTTP request headers as fundamental-value pairs. Information technology'south important to highlight that since we specify the content type header here, there is no need to provide a content blazon header manually while preparing the request. Not simply it'southward redundant, it's also dangerous as information technology could create conflicts and brand the server reject the request.

Next, let's first preparing the HTTP body. We'll start by calling the getHttpBody(withBoundary:) method:

Over again, since self is an optional, body might exist naught in example self is nil. So, in that case nosotros call the completion handler with another custom error and we return from the method.

Time to add the files to be uploaded to the HTTP body. Notice in the side by side line that nosotros pass the trunk variable with the "&" symbol as that's an inout parameter value:

failedFilenames is either nothing if all files are successfully added to the HTTP torso, or it contains the names of those files that failed to be appended to the body.

We should not forget to close the HTTP body properly:

We are prepare now to create the URL request:

The method we use here is already implemented in the RestManager class and we discussed about it in the previous tutorial. Observe that we laissez passer the URL with whatsoever potential query items (targetURL) and the HTTP body as arguments.

Finally, we'll create a new URLSession and an upload task to make the request. Upon completion, nosotros'll call the completion handler and we'll laissez passer a Results object with data regarding the results of the asking, and the failedFiles array.

The upload method is at present set:

There is one last thing to do earlier nosotros test out everything. To add the 2 new custom errors to the CustomError enum. Find it in the RestManager form and update information technology as shown side by side:

Update its extension right below appropriately with the description of the messages:

That's it! Time to upload files!

Testing File Uploading

The time to test file uploading has finally come. Switch to the ViewController.swift file and add together the following method definition:

For starters, we are going to upload a unmarried file only, and here nosotros will prepare the FileInfo object that volition contain its data.

Before nosotros go on, let me remind you that in the starter Xcode project y'all downloaded in that location are three files for testing: "sampleText.txt", "samplePDF.txt" and "sampleImage.pdf". We'll utilise the "sampleText.txt" here, but feel free to alter and apply any other file you want. Sample files exist in the application'due south bundle just for making the example as simple as possible, only in real apps the yous'll about always fetch them from the documents directory.

So, allow's start past creating a FileInfo object:

Run into that we are using the custom initializer we created in the FileInfo construction hither. However, in example y'all don't desire to initialize a FileInfo object that way and you prefer to manually set all values including the files contents, here's your alternative:

Notation: Server is implemented in a manner that requires the name attribute in every part of the multipart body to have the "uploadedFile" value. Therefore, that'southward the value that we'll be setting in the proper name property of each FileInfo object nosotros create here.

The URL where we'll make the asking to upload the file is: http://localhost:3000/upload. We will pass a URL object forth with an array that will contain the fileInfo object as arguments to a new method (we'll implement it right next):

upload(files:toURL:) is a small method responsible for making the asking every bit you can meet next. Nosotros could have put that code in the uploadSingleFile() method, but we'll use it again in a while when we'll upload multiple files. And so, nosotros'd improve avoid repeating code.

In the completion handler we don't do anything particular. We merely impress the HTTP status lawmaking, we brandish whatever potential errors, and the server's response subsequently nosotros convert it from JSON to a dictionary object. Of course, nosotros also impress the list of failed to exist uploaded files (in case in that location is any).

In the viewDidLoad() method telephone call the uploadSingleFile():

Run the app now and look at both in Xcode console and in the terminal where the server's output is printed. If yous followed everything step by step up until here, you should become this in Xcode:

At the same time, in terminal you should have the details of the uploaded file:

I wanted to make the small demo server and the file uploading procedure behave every bit much naturally equally possible, and then files sent to this server implementation are actually… being uploaded! In Finder, become to the Server directory that you downloaded in the starter package and then into the subdirectory called "uploads". The uploaded file is there which proves that file uploading is actually working!

Let'south make our testing more than interesting by as well sending boosted data along with the request. Right later on the initialization of the FileInfo object in the uploadSingleFile() method add together the following two lines:

Run the app again. In the terminal you should see the additional uploaded data besides:

Allow'south upload multiple files at present. Nosotros'll do that by creating a new method like to the previous one, with the departure existence that instead of initializing one FileInfo object, nosotros'll initialize three of them so we tin upload all sample files we have. Here it is:

At the end we telephone call once more the upload(files:toURL:) method which volition trigger the actual upload asking. Notice that the upload endpoint is different this fourth dimension ("multiupload"). To see it working, don't forget to telephone call it in the viewDidLoad():

This fourth dimension y'all should run across the names of the uploaded files in last:

Note that the current server implementation supports up to 10 simultaneous files to be uploaded. Of course y'all are gratis to change that limit according to your preference.

Summary

Starting in the previous tutorial where we created the commencement version of the RestManager class and continuing in this ane where we added the file uploading feature, we have managed to build a pocket-size and lightweight class capable of covering our needs in making web requests. "Multipart/class-information" content type and the way HTTP body is congenital can exist sometimes confusing, but if you intermission things downwardly then everything gets easy. I promise what I shared with you lot here today to be of some value, and I wish yous are enjoying RESTful services even more than now. You are always welcome to add together more features or adjust the electric current implementation according to your needs. Come across you next time!

For reference, you can download the full projection on GitHub.

griffithsflepes.blogspot.com

Source: https://www.appcoda.com/restful-api-tutorial-how-to-upload-files-to-server/

0 Response to "How to Build an Api for Uploading the Picture"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel