Http Post Example How to Upload File to Http Server

Welcome to a new, hopefully exciting tutorial! In a previous post I showed to yous the process of creating a custom class that manages web requests and RESTful APIs. Today, we volition go along building on information technology, every bit I would like to focus on a specific use example: How to upload files to a server!

Uploading files might not be ane of the most common things when dealing with spider web services. All the same, it tin be proved to be a boring task to perform when it'south fourth dimension to ship files to a server. In the implementation steps that follow we will endeavor to pause things down and shed calorie-free to the central points and the details of the uploading process. Before nosotros get in that location though, it'south necessary to take a quick discussion well-nigh some ground noesis that we all should accept on this topic.

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

Earlier nosotros commencement doing actual work, it's necessary some important things to be mentioned get-go. Let me start by saying that in order to upload files to a server, multipart/class-data is the content type that should be specified in the web asking. This content blazon allows to send files or large amounts of information in combination with other usual data that should be posted. "Multipart/form-data" content type tells to HTTP request that posted information should be cleaved into parts, as if they were to be posted by a web form that expects from users to fill 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 function starts and where information technology ends. For that purpose, a special and unique string is provided along with the content type, called boundary. That string should not occur in the actual data, then it must exist as much unique as possible. It e'er starts with ii dashes ("–"), with an arbitrary combination of other alphanumeric characters coming later on. Usually, boundaries offset with multiple dashes, and then they have an alphanumeric suffix (e.one thousand. —————–abc123).

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

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

Discover how everything mentioned in the previous paragraphs is used. At first, the "multipart/form-information" content blazon is specified forth with the boundary string that separates the information parts. See how boundary indicates the starting time of each part and also come across how semicolon (";") separates attributes in headers. Line breaks are as well important when building a HTTP body such the above one. In unmarried fields, an empty line exists between the "Content-Disposition" header and the actual field value, while the boundary of the next part comes correct later in the next line. In file parts, the "filename" aspect contains the proper name of the file, while an boosted empty line exists between the file contents and the side by side boundary. The body ending is highlighted past the boundary, plus two more than dashes as a suffix to information technology.

I am encouraging you to take a expect at the W3C HTML Specification and read more about encoding content types and the "multipart/form-information" peculiarly. You don't have to terminate at that place of grade; a general search on the web volition return lots of resource to read most this topic.

About The Demo App

So, as I said in the beginning of this mail, we are going to keep building on the custom class we created in the previous tutorial, called RestManager. To get started, please download a starter bundle which contains a Xcode project with that grade and 1 more directory with a demo server implementation (see next part). In Xcode project you will find three files that nosotros'll use to test file uploading subsequently we cease all implementation steps:

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

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

Most The Server

After we finish implementing all the new lawmaking we'll meet in the post-obit parts, we'll demand to test if file uploading is really working. For that purpose, a simple server implemented in Node.js is included in the starter package that yous downloaded; you volition notice information technology in the Server subdirectory. You can proceed it in the location that currently is, or copy it anywhere else you lot want in your disk.

In order to run the server, you must accept Node.js installed on your computer. If you don't, please check here or hither on how to practise that. Open Terminal and type the following command:

In that location is a infinite character after the cd command. Then switch to Finder, and drag and drop the Server directory to last and press the Return key:

By doing and so, yous don't have to type the path to the server directory; it's automatically appended to the command in final.

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

This command volition show the electric current directory contents, and if yous see something similar to the side by side one, then you're only fine:

To commencement the server just type:

You should see the message:

Server started successfully on port 3000!

The server is now running at accost http://localhost:3000. You tin can as well verify that if yous paste that address in a new tab in your browser. You'll see a message coming from the server.

Note: 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 not 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 project so you will encounter no trouble in testing the app afterwards.

Representing Files

The first affair 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 following data available at the time of the HTTP body preparation:

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

Obviously, all that information could be stored in a dictionary, only that wouldn't be the all-time approach in Swift. To do 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 stop of it. Yous will observe the following empty extension:

This is where we'll add together nigh all new code regarding the file uploading characteristic. Inside this extension, add the following construction:

The 4 properties will keep the data described earlier. Equally you volition meet later, if any of the above properties is nil the file won't be 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 be necessary to provide the actual file contents when creating a FileInfo object. Specifying the URL of the file will be plenty. File contents will be read in the in a higher place initializer.

Creating The Boundary

Having a solution on our hands about how to represent files, let's create a method which will be responsible of creating the purlieus cord. Think that a boundary must be unique and definitely not an ordinary cord that could exist potentially found in the bodily data that volition be uploaded. As I said in the beginning of the postal service, even though boundaries start with ii dashes ("–"), they usually take several more dashes following and a random alphanumeric string at the end. That's not mandatory, just it'due south the logic we will follow here.

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

I will prove you two different ways to generate the random boundary string.

Using A UUID String

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

The above will generate something similar to this:

Let'due south get rid of the dashes in that string, and let'south convert all messages to lowercase:

The original UUID will now look similar this:

Let'south construct the purlieus string. It volition be a concatenation of 20 dashes at the beginning and the transformed UUID value:

If you similar exaggerating, add the electric current timestamp to the end also:

A boundary cord created with the above will expect like:

Well, that looks quite unique and random, no?

Hither's the implementation of the entire method:

Using Random Characters

Equally an alternative to the above we can create a machinery which will pick random characters from a collection of available characters, and using them to class a string which volition exist appended to the purlieus string. The drove of available characters will be parted by all letters ranging from upper cased "A" to "Z", lower cased "a" to "z", and all digits from "0" to "ix".

We won't actually need to difficult-code anything, as we can programmatically construct everything. We will be based on the ASCII tabular array for that.

We'll start by specifying the range of the lower cased characters ("a" to "z") in the ASCII tabular array as shown beneath:

The in a higher place is equivalent to this:

where 97 is the position of the "a" character and "122" is the position of the "z" grapheme in the ASCII table.

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

Similarly, nosotros get the ranges of the upper cased A-Z and of the digits:

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

If nosotros impress the value of the sequenceOfRanges to the panel at runtime nosotros'll get this:

Even though it'southward not obvious unless someone looks upwards for information technology, the above tin be easily converted into a String value:

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

That absurd! Let's generate a string of 20 random characters now:

At outset we initialize a string value called randomString. Then, we create a loop that will be executed xx times. In information technology, we choice 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 safe to force unwrap the value of the randomElement() method, every bit information technology returns nil only 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:

Utilise the implementation you lot prefer the most. The 2nd i is more "Swifty" simply information technology requires a flake of more code. At the terminate of the day, 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 two dashes ("–"). These two dashes are not included in the dashes of the purlieus cord nosotros generated in both approaches here. This string will exist provided as-is to the asking as a request header forth with the content blazon and server volition try to locate information technology subsequently the two dashes prefix. Too, a boundary cord can exist with no dashes at all; we merely add them to minimize the possibility to notice similar string in the uploaded information. As you lot will run across later, the ii dashes prefix volition be manually appended whenever necessary.

Extending Data Structure

Our side by side steps involve the training of the HTTP torso using any arbitrary data provided to the class, equally well every bit using the files data. But before we get into that, we volition extend the Data structure and we will create the following generic method:

The purpose of this method is to let u.s.a. easily append the values of the values collection to the data object that calls information technology. And equally you'll see, we'll be interested for String and Data types merely.

Just for clarification, we could avert implementing this method. Notwithstanding, the code that nosotros volition add to it would have to be repeated multiple times in different points in the RestManager form, and that definitely would not be a wise movement.

So, to continue get to the end of the RestManager.swift file where you lot 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 type of the given values. Let's beginning with the Cord type. In this case, we'll make a loop to access all values in the values parameter collection:

In each repetition nosotros volition convert the cord value into a Information object and we volition append it to the local newData variable. If for some reason the string value cannot exist converted into a Data object, we'll set the status flag to fake and we'll pause the loop.

We will follow a quite similar approach in case of Information input values. Of course, there is no need to initialize any new Information object or make a conversion of any type. We are appending one data value to some other:

Lastly, permit's indicate that nosotros don't intendance about any other type of values:

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

At the end, we should not forget to return the status as the issue of the method:

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

Creating the HTTP Body

In the current implementation of RestManager at that place is a method named getHttpBody(). Its purpose is to prepare the HTTP torso with the data that volition exist posted to the server. Although this method works not bad in any other example, unfortunately information technology's not of much help in case of file uploading. There is the boundary string we have to have into account, as well 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 as an argument (also known every bit method overloading).

In the new extension of the RestManager class, correct below the createBoundary method, add the post-obit:

Proceed in mind 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's the data that would exist unremarkably submitted if there were no files to upload at the aforementioned time, and it's kept in the httpBodyParameters property (as a reminder, httpBodyParameters is a belongings in the RestManager class and information technology'southward of RestEntity blazon, a custom structure – observe it in RestManager and read more in the previous tutorial almost it).

httpBodyParameters has a method called allValues() and returns all data as a dictionary (a [String: String] dictionary). Nosotros'll utilise it to access all values that should exist sent to the server and append them to the body variable. Right later the var body = Data() line add the following:

A small cease here now as we have to hash out what exactly we'll be appending to the body. Let's come across again part of the example presented in the beginning of this mail:

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

  • At offset there is the boundary cord, and right afterward that a line break. In HTTP headers, a line break is marked with "\r\northward" (carriage return and new line character), not just the "\n" that nosotros are mostly used to. Programmatically, this could be written like: "--\(boundary)\r\n" (run across the 2 dashes before the boundary string).
  • Next, there is the "Content-Disposition" header with the proper noun aspect only in information technology. Header is followed by a line break ii times. We could write this like so: "Content-Disposition: form-data; name=\"\(central)\"\r\n\r\due north".
  • Lastly, it's the actual value followed past a line pause. That'southward easy: "\(value)\r\northward".

We will add together the code that represents each step described above into an assortment:

Nosotros will apply for kickoff time the append(values:) custom method we implemented in the previous stride in club to convert these strings into Data objects and suspend them to the trunk variable:

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

We'll use the results of this method in a while. For now, we have to add the files data to the HTTP trunk as well.

Adding Files To HTTP Body

I could say that the getHttpBody(withBoundary:) method we just implemented forth with the new 1 we will implement here consist of the nigh important part of the overall work we have to do in order to brand file uploading possible. And that would exist pretty much true, as nosotros've congenital all the helper methods we need and at present nosotros are dealing with the cadre functionality.

So, continuing on edifice the HTTP torso, let'south ascertain the following new method:

Let'south talk first almost the parameters. The first 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 be fabricated to that object inside this method will be reflected out of it besides because information technology's marked with the inout keyword. The last parameter is the purlieus string, as we necessarily need information technology to separate information parts.

You might be wondering why this method returns an optional assortment of String values. Well, in example there are files whose information cannot be added to the HTTP body, then we'll keep their names into an array, which in turn the method will return. In normal weather condition this method should render null, pregnant that data from all files was successfully appended to the HTTP body data.

Let's beginning adding some code, with the first 1 being the following local variables:

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

Let'southward start a loop at present:

The beginning thing we take to do is to make certain that all properties of each file object have actual values then we tin can proceed:

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

Now, let's encounter once again the example presented in the get-go of the tutorial so we understand what we take to do:

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

  • At start there is the boundary with the line break at the end. We already know how to write that in code.
  • Next, we have the "Content-Disposition" header. The addition here (comparing to the header in the previous part) is the new filename aspect which contains the actual file name. In code such a header is written like this: "Content-Disposition: class-data; name=\"\(proper noun)\"; filename=\"\(filename)\"\r\n".
  • Right after we have the content type of the file. See all the available MIME Media Types. In code this is like so: "Content-Blazon: \(mimetype)\r\n\r\n".

Let'due south make a break here and let's append all the higher up to an array:

Let's catechumen all strings in that assortment into Data objects and append them to the data variable:

Let's proceed where we had stopped from. The side by side item in a file function is the bodily file contents. Think that file contents are represented by the fileContents property in a FileInfo object, which is a Information object. So far nosotros were dealing with strings just. File contents must be appended to the data variable as well:

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

Lastly, notice in the above example that there is an empty line correct afterward the file contents which should be added to the information as well:

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

Encounter that we used the custom append(values:) custom method three times in a row here. I hope you agree that its implementation was meaningful since we use it again and once more.

Side by side, let's check the status value for each file. While still being on the loop:

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

One last affair remaining, to return the failedFilenames from the method:

Our new method should now look like this:

Endmost The HTTP Torso

Now that we created methods which build the HTTP body by appending any post data and file information, nosotros must create one more than which will close the body. Remember that in "multipart/form-information" the HTTP body closing is marked by the purlieus string and 2 dashes equally a suffix to information technology:

As you can guess, doing so doesn't require much of work as all information technology takes is this:

For i more time here the body parameter is marked equally inout, so the information argument will be passed by reference and the changes made to it inside this method will become visible to the caller too. Too that, notice the line breaks before and subsequently the closing string which ensure that the closing boundary will be the only content in the line.

It's really of import not to forget to call this method and indicate the end of parts in the multipart HTTP body.

Uploading Files

It's about time to put everything together and brand file uploading possible. The method we'll write here will exist public, so you can go and add it to the height of the class forth 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. We won't run anything on the main thread since file uploading could have significant corporeality of time and we don't want apps to show frozen. In code that means:

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

The start actual activeness we accept to take is to add together whatsoever URL query parameters specified in the urlQueryParameters holding to the URL. This volition happen by calling the addURLQueryParameters(toURL:) method which we implemented in the previous tutorial:

Adjacent, let's call the createBoundary() method we implemented today and let'due south create the boundary string:

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

Let's get going, and in the adjacent step let's add the "multipart/form-data" forth with the boundary string to the drove of the request headers:

To refresh your memory, requestHttpHeaders is a RestEntity property which keeps all HTTP asking headers as key-value pairs. It's important to highlight that since we specify the content type header here, there is no need to provide a content type header manually while preparing the request. Not only it's redundant, it'southward also unsafe every bit it could create conflicts and make the server reject the request.

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

One time once again, since self is an optional, body might be zilch in case cocky is nil. So, in that instance nosotros phone call the completion handler with another custom mistake and we return from the method.

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

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

We should non forget to shut the HTTP body properly:

We are ready now to create the URL asking:

The method we use here is already implemented in the RestManager grade and we discussed about information technology in the previous tutorial. Observe that we pass the URL with any potential query items (targetURL) and the HTTP trunk every bit arguments.

Finally, we'll create a new URLSession and an upload task to make the asking. 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 assortment.

The upload method is at present fix:

In that location is one terminal thing to practice before we exam out everything. To add the 2 new custom errors to the CustomError enum. Find it in the RestManager form and update it as shown next:

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

That's it! Time to upload files!

Testing File Uploading

The fourth dimension 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 single file just, and here we will prepare the FileInfo object that volition contain its data.

Before we go on, allow me remind you that in the starter Xcode projection yous downloaded there are three files for testing: "sampleText.txt", "samplePDF.txt" and "sampleImage.pdf". We'll apply the "sampleText.txt" here, but feel complimentary to change and use whatever other file yous want. Sample files be in the application'southward bundle just for making the example equally elementary as possible, only in real apps the y'all'll almost always fetch them from the documents directory.

And so, permit's first past creating a FileInfo object:

Run across that nosotros are using the custom initializer nosotros created in the FileInfo structure hither. However, in case y'all don't desire to initialize a FileInfo object that style and you prefer to manually set all values including the files contents, here'south your alternative:

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

The URL where we'll make the request to upload the file is: http://localhost:3000/upload. We will laissez passer a URL object along with an array that will comprise the fileInfo object every bit arguments to a new method (we'll implement it right side by side):

upload(files:toURL:) is a modest method responsible for making the asking every bit you tin can see next. We could have put that lawmaking in the uploadSingleFile() method, simply we'll apply information technology again in a while when nosotros'll upload multiple files. So, nosotros'd amend avoid repeating code.

In the completion handler nosotros don't exercise anything particular. We but print the HTTP status code, nosotros display any potential errors, and the server'southward response after we catechumen it from JSON to a dictionary object. Of class, we too print the list of failed to be uploaded files (in case there is any).

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

Run the app now and await at both in Xcode console and in the last where the server's output is printed. If you followed everything stride by footstep up until here, you should get this in Xcode:

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

I wanted to make the modest demo server and the file uploading process conduct as much naturally as possible, so files sent to this server implementation are actually… existence uploaded! In Finder, go to the Server directory that you downloaded in the starter package and so into the subdirectory chosen "uploads". The uploaded file is there which proves that file uploading is really working!

Let'due south make our testing more interesting by also sending additional data along with the asking. Right later on the initialization of the FileInfo object in the uploadSingleFile() method add the post-obit 2 lines:

Run the app again. In the terminal you should come across the additional uploaded data as well:

Let'due south upload multiple files now. We'll do that by creating a new method similar to the previous 1, with the difference existence that instead of initializing ane FileInfo object, we'll initialize 3 of them so nosotros can upload all sample files nosotros have. Here it is:

At the finish nosotros call again the upload(files:toURL:) method which will trigger the bodily upload request. Observe that the upload endpoint is different this time ("multiupload"). To encounter it working, don't forget to call it in the viewDidLoad():

This time you should run across the names of the uploaded files in final:

Note that the current server implementation supports up to x simultaneous files to be uploaded. Of grade you are free to change that limit according to your preference.

Summary

Starting in the previous tutorial where we created the first version of the RestManager course and continuing in this one where nosotros added the file uploading characteristic, we accept managed to build a minor and lightweight course capable of covering our needs in making spider web requests. "Multipart/form-information" content type and the way HTTP body is congenital tin can be sometimes disruptive, but if you lot interruption things down then everything gets easy. I hope what I shared with you lot here today to exist of some value, and I wish you are enjoying RESTful services even more at present. You are always welcome to add more features or conform the current implementation according to your needs. Encounter y'all next time!

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

forbeswouesed.blogspot.com

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

0 Response to "Http Post Example How to Upload File to Http Server"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel