Upload Files to Webserver Using Rest Api

Welcome to a new, hopefully exciting tutorial! In a previous post I showed to you the process of creating a custom class that manages spider web requests and RESTful APIs. Today, we will proceed building on it, as I would like to focus on a specific use case: How to upload files to a server!

Uploading files might not be one of the virtually common things when dealing with web services. Nonetheless, information technology can be 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 effort to interruption things downward and shed lite to the key points and the details of the uploading process. Before we get there though, it's necessary to have a quick discussion almost some footing knowledge that nosotros all should have on this topic.

A Quick Intro To "Multipart/form-data" Content Blazon

Before we commencement doing bodily piece of work, it's necessary some important things to exist mentioned showtime. Let me start by saying that in order to upload files to a server, multipart/form-data is the content type that should be specified in the web request. This content type allows to send files or large amounts of information in combination with other usual data that should be posted. "Multipart/course-data" content type tells to HTTP request that posted information should exist broken into parts, every bit if they were to be posted by a web form that expects from users to make full in various fields and select files that should be submitted to a server.

Since posted data is broken into parts, information technology's necessary for the server to know where a part starts and where it ends. For that purpose, a special and unique cord is provided along with the content blazon, called purlieus. That string should not occur in the actual data, so it must be as much unique as possible. It always starts with two dashes ("–"), with an arbitrary combination of other alphanumeric characters coming later on. Usually, boundaries start with multiple dashes, and then they have an alphanumeric suffix (eastward.1000. —————–abc123).

Each office of a multipart body necessarily starts with a Content-Disposition header, with the form-data value coming in pair with it. An aspect called "name" should likewise be provided in the header, equally it specifies the name of the part. Discover that names don't need to be unique, and sometimes server sets the rules that apply to the "name" aspect. These two key-value pairs are enough when adding single data (meaning no files) to the request'southward HTTP body. When appending files data, the filename should be besides included in the "Content-Disposition" header with the original name of the file, too as the content blazon (MIME type) of each file that is near to exist uploaded.

The following is a imitation case of a HTTP request body that uses the "multipart/grade-data" content blazon:

Notice how everything mentioned in the previous paragraphs is used. At start, the "multipart/form-data" content type is specified forth with the boundary string that separates the data parts. Run into how purlieus indicates the first of each part and too see how semicolon (";") separates attributes in headers. Line breaks are also important when building a HTTP trunk such the to a higher place i. In unmarried fields, an empty line exists betwixt the "Content-Disposition" header and the actual field value, while the boundary of the next function comes correct afterward in the next line. In file parts, the "filename" attribute contains the name of the file, while an additional empty line exists betwixt the file contents and the adjacent boundary. The trunk catastrophe is highlighted by the boundary, plus two more dashes as a suffix to information technology.

I am encouraging y'all to accept a await at the W3C HTML Specification and read more about encoding content types and the "multipart/form-data" especially. You don't have to cease in that location of course; a general search on the web will return lots of resources to read about this topic.

Well-nigh The Demo App

So, as I said in the beginning of this mail service, nosotros are going to keep building on the custom class nosotros created in the previous tutorial, called RestManager. To become started, please download a starter packet which contains a Xcode projection with that class and one more than directory with a demo server implementation (see next office). In Xcode project you will discover three files that we'll employ to test file uploading afterwards 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 paradigm file named SampleImage.jpg downloaded from Pexels (Photo by Oleg Magni from Pexels).

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

Almost The Server

After we end implementing all the new code we'll run into in the following parts, we'll need to test if file uploading is actually working. For that purpose, a simple server implemented in Node.js is included in the starter package that you downloaded; y'all will find it in the Server subdirectory. You can keep information technology in the location that currently is, or copy it anywhere else you want in your disk.

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

There is a infinite graphic symbol after the cd command. Then switch to Finder, and drag and drop the Server directory to terminal and press the Return key:

By doing so, you don't have to blazon the path to the server directory; it'due south automatically appended to the command in final.

To verify that you are successfully in the server directory, merely type:

This command will show the electric current directory contents, and if you see something similar to the next one, then you're just fine:

To start the server just type:

You should see the bulletin:

Server started successfully on port 3000!

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

Annotation: If you are already running another server at port 3000, edit the index.js file and fix a custom port number to the port variable. And then restart the server with the node alphabetize.js control.

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

Representing Files

The offset thing nosotros need to take intendance of is how files are going to be represented in the RestManager grade. For any file that is about to be uploaded, we need to accept the following data available at the time of the HTTP body grooming:

  • The actual file contents.
  • The original file proper name. Remember that the filename aspect must exist in the "Content-Disposition" header of each part that represents a file.
  • The part's name for the proper name aspect in the "Content-Disposition" header.
  • The content blazon (MIME blazon) of the file.

Obviously, all that information could exist stored in a dictionary, just that wouldn't exist the best approach in Swift. To practise information technology better, let's create a struct which we'll call FileInfo. Open the RestManager.swift file in the starter Xcode project, and get to the end of it. Yous volition notice the following empty extension:

This is where we'll add together most all new code regarding the file uploading feature. Within this extension, add the following construction:

The 4 properties will keep the data described earlier. As yous will meet subsequently, if any of the higher up properties is nil the file won't be added to the HTTP trunk for submission to the server.

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

With this initializer, information technology won't be necessary to provide the actual file contents when creating a FileInfo object. Specifying the URL of the file will be enough. File contents will be read in the to a higher place initializer.

Creating The Boundary

Having a solution on our hands well-nigh how to correspond files, let's create a method which will be responsible of creating the purlieus string. Remember that a boundary must exist unique and definitely non an ordinary string that could be potentially constitute in the actual data that will be uploaded. As I said in the offset of the post, even though boundaries start with two dashes ("–"), they usually have several more dashes following and a random alphanumeric cord at the terminate. That's not mandatory, only it'south the logic we will follow here.

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

I will evidence yous two different means to generate the random boundary string.

Using A UUID String

The fastest mode to get a random cord is to generate a UUID value:

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

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

The original UUID volition at present expect like this:

Let's construct the boundary string. Information technology will be a concatenation of 20 dashes at the beginning and the transformed UUID value:

If yous like exaggerating, add together the current timestamp to the stop as well:

A boundary string created with the above will await similar:

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 can create a mechanism which will pick random characters from a collection of available characters, and using them to class a string which will be appended to the boundary string. The drove of available characters will exist parted by all letters ranging from upper cased "A" to "Z", lower cased "a" to "z", and all digits from "0" to "9".

We won't actually need to difficult-lawmaking anything, every bit we can programmatically construct everything. We will be based on the ASCII table for that.

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

The above is equivalent to this:

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

However, the second line of code requires from us to search for an ASCII table online so locate the position of the characters we are interested in into the table. Okay, it'southward easy, but it'southward definitely not the recommended way, since we can get the values nosotros desire by using the UInt8(ascii:) initializer. And that'southward we do in the first place.

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

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

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

Even though it's not obvious unless someone looks up for it, the in a higher place tin be hands converted into a String value:

Information struct provides several initializers for creating a data object and there is ane among them that accepts a sequence every bit an statement, exactly as we practice in the Data(sequenceOfRanges) expression. From that data object, we can create the following string which is assigned to the toString constant:

That cool! Let's generate a string of twenty random characters at present:

At first we initialize a string value called randomString. So, we create a loop that volition be executed 20 times. In it, we pick a random character 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.

Note that is safety to forcefulness unwrap the value of the randomElement() method, equally it returns nil but in cases of empty collections. Hither 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 purlieus:

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

Utilise the implementation yous prefer the most. The second one is more "Swifty" only it requires a bit of more code. At the stop of the twenty-four hour period, both approaches are going to work equally well.

An important note: I've mentioned already that the boundary cord which separates the parts of a multipart body starts with ii dashes ("–"). These two dashes are not included in the dashes of the purlieus string nosotros generated in both approaches here. This string will be provided every bit-is to the request every bit a asking header along with the content type and server will try to locate it after the 2 dashes prefix. As well, a purlieus string can exist with no dashes at all; we just add together them to minimize the possibility to notice similar string in the uploaded data. As you will see later, the ii dashes prefix will be manually appended whenever necessary.

Extending Data Structure

Our next steps involve the preparation of the HTTP torso using any capricious data provided to the class, as well as using the files data. But earlier nosotros get into that, we volition extend the Information structure and we will create the following generic method:

The purpose of this method is to permit us easily append the values of the values drove to the data object that calls information technology. And as you'll see, we'll be interested for String and Data types but.

Just for clarification, nosotros 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 course, and that definitely would non be a wise move.

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

Add together the new method's definition in it:

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

Adjacent, nosotros'll distinguish the type of the given values. Let's start with the String type. In this instance, we'll brand a loop to access all values in the values parameter collection:

In each repetition nosotros will catechumen the cord value into a Data object and nosotros will append it to the local newData variable. If for some reason the string value cannot exist converted into a Information object, we'll fix the status flag to faux and we'll break the loop.

We will follow a quite similar approach in case of Data input values. Of class, there is no demand to initialize any new Data object or make a conversion of any blazon. Nosotros are appending one data value to some other:

Lastly, allow's indicate that we don't care nigh any other type of values:

Adjacent, nosotros'll bank check the status value. If it's true, then we can append the newData local variable to the self object (the Data object that is used to call this method).

At the finish, we should not forget to return the status equally the outcome of the method:

Here's the entire implementation. We are going to put information technology in activity starting from the next part.

Creating the HTTP Trunk

In the current implementation of RestManager there is a method named getHttpBody(). Its purpose is to prepare the HTTP torso with the information that will exist posted to the server. Although this method works great in whatsoever other instance, unfortunately it's not of much assistance in example of file uploading. There is the boundary cord we take to take into business relationship, as well equally the special headers and formatting required when using the "multipart/form-information" content type. To serve our new needs, we'll implement a similarly named method which will be accepting the purlieus cord as an argument (also known as method overloading).

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

Keep in heed that the HTTP body must be 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 nosotros'll deal with any data that should be posted to the server except for files. That'due south the data that would be usually submitted if there were no files to upload at the same time, and it'due south kept in the httpBodyParameters property (as a reminder, httpBodyParameters is a property in the RestManager class and it's of RestEntity type, a custom structure – find it in RestManager and read more in the previous tutorial about it).

httpBodyParameters has a method chosen allValues() and returns all data every bit a dictionary (a [Cord: String] lexicon). We'll apply it to admission all values that should exist sent to the server and append them to the body variable. Right subsequently the var trunk = Data() line add together the following:

A minor cease here now as nosotros take to hash out what exactly nosotros'll be appending to the trunk. Let's see over again function of the example presented in the beginning of this post:

In this example the data is the username and the password. The following utilize to each piece of data:

  • At first there is the purlieus string, and right after that a line suspension. In HTTP headers, a line break is marked with "\r\n" (railroad vehicle return and new line character), not but the "\n" that we are mostly used to. Programmatically, this could exist written like: "--\(purlieus)\r\n" (come across the 2 dashes before the boundary string).
  • Next, at that place is the "Content-Disposition" header with the name attribute only in information technology. Header is followed by a line interruption ii times. Nosotros could write this similar so: "Content-Disposition: form-information; name=\"\(central)\"\r\n\r\due north".
  • Lastly, it's the actual value followed by a line interruption. That's easy: "\(value)\r\n".

Nosotros will add together the code that represents each stride described higher up into an array:

Nosotros volition use for first time the append(values:) custom method we implemented in the previous pace in order to catechumen these strings into Data objects and append them to the body variable:

And that's the last thing we had to do in this method. Let's see it altogether now:

We'll apply the results of this method in a while. For now, nosotros take to add the files information to the HTTP body as well.

Calculation Files To HTTP Body

One could say that the getHttpBody(withBoundary:) method we just implemented along with the new 1 nosotros volition implement here consist of the most important part of the overall work we have to do in guild to make file uploading possible. And that would be pretty much true, equally nosotros've built all the helper methods we need and now nosotros are dealing with the core functionality.

So, continuing on building the HTTP body, let's define the following new method:

Let's talk showtime about the parameters. The showtime ane is a drove of FileInfo objects, and information technology contains the data for all files that are about to be uploaded. The 2nd parameter value is the data object that represents the HTTP body. Any changes that will be made to that object inside this method will be reflected out of it every bit well because information technology's marked with the inout keyword. The concluding parameter is the purlieus string, equally we necessarily demand information technology to dissever data parts.

Y'all might exist wondering why this method returns an optional array of String values. Well, in case in that location are files whose information cannot exist added to the HTTP body, so we'll keep their names into an array, which in plough the method will return. In normal conditions this method should render nil, significant that information from all files was successfully appended to the HTTP torso data.

Let's start adding some lawmaking, with the first 1 being the following local variables:

condition volition betoken whether all pieces of data for each single file in the files collection were successfully combined in one Data object, which can exist then appended to the body inout parameter. If status is fake, nosotros'll exist appending the name of the matching file to the failedFilenames array.

Let's commencement a loop now:

The kickoff thing nosotros take to do is to make sure that all properties of each file object take actual values and so we tin go along:

Adjacent, we volition set the initial value of the condition flag on each repetition of the loop to false, and we'll initialize a new Data object.

Now, let's see again the example presented in the beginning of the tutorial then we empathise what we have to practice:

Going step by step through the lines that describe a file part:

  • At kickoff there is the boundary with the line break at the end. We already know how to write that in code.
  • Adjacent, we have the "Content-Disposition" header. The improver here (comparing to the header in the previous function) is the new filename attribute which contains the actual file name. In code such a header is written like this: "Content-Disposition: class-data; name=\"\(name)\"; filename=\"\(filename)\"\r\n".
  • Right afterward we have the content type of the file. Meet all the available MIME Media Types. In lawmaking this is like then: "Content-Type: \(mimetype)\r\due north\r\due north".

Permit'southward make a break hither and let's append all the higher up to an array:

Let'southward convert all strings in that array into Information objects and suspend them to the data variable:

Let's proceed where nosotros had stopped from. The next particular in a file role is the actual file contents. Call up that file contents are represented by the fileContents property in a FileInfo object, which is a Information object. So far we were dealing with strings only. File contents must be appended to the information variable too:

Retrieve that suspend(values:) method expects for an array of values, so it's necessary to include content into the array's opening and closing brackets higher up.

Lastly, notice in the to a higher place case that at that place is an empty line right afterward the file contents which should be added to the data likewise:

These three weather we wrote must be embedded into each other. If all of them are true, then all data pieces for the current file were successfully added to the data object, and we'll indicate 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 again and over again.

Next, permit'south check the status value for each file. While still existence on the loop:

If condition is true, we append the data variable to the body which represents the HTTP trunk. If non, then we initialize the failedFilenames array in example it's non initialized already, and we keep the proper noun of the current file in it.

Ane final matter remaining, to return the failedFilenames from the method:

Our new method should now expect like this:

Endmost The HTTP Body

Now that we created methods which build the HTTP body past appending any post information and file information, we must create one more which will close the body. Remember that in "multipart/form-data" the HTTP trunk closing is marked by the boundary string and two dashes as a suffix to it:

Every bit yous can guess, doing and so doesn't require much of work as all it takes is this:

For one more fourth dimension here the body parameter is marked as inout, so the information statement will be passed by reference and the changes made to information technology inside this method will become visible to the caller too. Besides that, notice the line breaks before and after the closing string which ensure that the closing boundary will be the only content in the line.

It's really important not to forget to call this method and point the cease of parts in the multipart HTTP torso.

Uploading Files

Information technology'southward virtually time to put everything together and make file uploading possible. The method we'll write hither will exist public, so you can go and add it to the top of the form along with other two public methods existing already. Here is its definition:

In accordance to what we did to the other two existing public methods, we are going to perform all deportment in this method asynchronously. Nosotros won't run anything on the main thread since file uploading could take significant amount of fourth dimension and we don't want apps to prove frozen. In code that means:

With userInitiated value in the quality of service parameter we give our task a relatively high priority in execution. Note that nosotros marking cocky as weak in the closure since the RestManager instance used to perform the file uploading can potentially become nil, and that practically ways that cocky is from now on an optional. This introduces a couple of new needs equally you will run across next.

The first actual action we have to have is to add any URL query parameters specified in the urlQueryParameters belongings to the URL. This volition happen by calling the addURLQueryParameters(toURL:) method which we implemented in the previous tutorial:

Next, let's call the createBoundary() method we implemented today and let's create the purlieus string:

Notice that since self is used every bit an optional, purlieus becomes an optional value also, regardless of the fact that createBoundary() does not return an optional. So, in case at that place's no boundary cord to go on, nosotros call the completion handler passing the mistake shown above and we return from the method. This custom error doesn't exist still in the class, we'll add it in a while.

Let's get going, and in the side by side stride allow's add together the "multipart/course-data" along with the boundary cord to the drove of the request headers:

To refresh your memory, requestHttpHeaders is a RestEntity holding which keeps all HTTP request headers equally central-value pairs. It'south important to highlight that since nosotros specify the content type header hither, there is no need to provide a content type header manually while preparing the request. Not just information technology's redundant, it's also unsafe as information technology could create conflicts and make the server turn down the asking.

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

Once over again, since self is an optional, torso might be nil in case cocky is aught. So, in that instance we phone call the completion handler with another custom error and we return from the method.

Time to add together the files to be uploaded to the HTTP torso. Discover in the next line that nosotros pass the body variable with the "&" symbol as that's an inout parameter value:

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

We should not forget to close the HTTP body properly:

We are ready now to create the URL asking:

The method nosotros use here is already implemented in the RestManager form and we discussed about it in the previous tutorial. Notice that we pass the URL with whatever potential query items (targetURL) and the HTTP body every bit arguments.

Finally, we'll create a new URLSession and an upload task to brand the request. Upon completion, we'll call the completion handler and we'll pass a Results object with information regarding the results of the asking, and the failedFiles array.

The upload method is now ready:

There is one terminal affair to do before we test out everything. To add together the 2 new custom errors to the CustomError enum. Find it in the RestManager grade and update it as shown next:

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

That'southward it! Time to upload files!

Testing File Uploading

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

For starters, we are going to upload a single file merely, and here we will prepare the FileInfo object that will contain its data.

Before we go on, permit me remind yous that in the starter Xcode projection you downloaded there are three files for testing: "sampleText.txt", "samplePDF.txt" and "sampleImage.pdf". We'll utilize the "sampleText.txt" here, just feel costless to change and use any other file you want. Sample files exist in the application'southward parcel only for making the example every bit simple every bit possible, but in real apps the you lot'll almost always fetch them from the documents directory.

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

See that we are using the custom initializer we created in the FileInfo structure here. However, in case you don't want to initialize a FileInfo object that manner and you prefer to manually set all values including the files contents, hither's your alternative:

Note: Server is implemented in a way that requires the name aspect in every part of the multipart body to accept the "uploadedFile" value. Therefore, that's the value that we'll be setting in the proper name property of each FileInfo object nosotros create hither.

The URL where we'll make the request to upload the file is: http://localhost:3000/upload. Nosotros volition pass a URL object along with an array that volition contain the fileInfo object as arguments to a new method (we'll implement information technology right adjacent):

upload(files:toURL:) is a pocket-size method responsible for making the request as you tin encounter next. We could have put that lawmaking in the uploadSingleFile() method, but we'll use it again in a while when we'll upload multiple files. So, we'd better avert repeating lawmaking.

In the completion handler we don't practice anything particular. We just print the HTTP status lawmaking, we display any potential errors, and the server'south response after nosotros convert it from JSON to a dictionary object. Of class, we also print the list of failed to be uploaded files (in instance there is any).

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

Run the app at present and look at both in Xcode console and in the last where the server's output is printed. If you followed everything step by pace up until hither, you lot should go this in Xcode:

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

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

Let's make our testing more interesting by also sending boosted data along with the request. Right afterwards the initialization of the FileInfo object in the uploadSingleFile() method add the following two lines:

Run the app once again. In the last yous should come across the additional uploaded data also:

Let'due south upload multiple files at present. We'll do that by creating a new method like to the previous one, with the difference being that instead of initializing one FileInfo object, nosotros'll initialize three of them so we tin upload all sample files we have. Hither information technology is:

At the end nosotros phone call over again the upload(files:toURL:) method which will trigger the actual upload asking. Notice that the upload endpoint is different this fourth dimension ("multiupload"). To see it working, don't forget to phone call it in the viewDidLoad():

This time y'all should see the names of the uploaded files in terminal:

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

Summary

Starting in the previous tutorial where we created the start version of the RestManager grade and continuing in this one where nosotros added the file uploading feature, we have managed to build a minor and lightweight course capable of covering our needs in making spider web requests. "Multipart/form-data" content type and the way HTTP torso is built can be sometimes confusing, but if you lot break things down and so everything gets easy. I hope what I shared with you hither today to be of some value, and I wish yous are enjoying RESTful services even more now. You lot are e'er welcome to add more features or adapt the electric current implementation according to your needs. See you lot side by side fourth dimension!

For reference, you can download the total project on GitHub.

raderswelf1981.blogspot.com

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

0 Response to "Upload Files to Webserver Using Rest Api"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel