banner



What Data Takes When Upload Image To Post Api Call

Welcome to a new, hopefully heady tutorial! In a previous mail service I showed to you lot the process of creating a custom class that manages web requests and RESTful APIs. Today, nosotros volition continue 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 one of the about mutual things when dealing with web services. However, it can exist proved to exist a tedious chore to perform when it'southward time to send files to a server. In the implementation steps that follow we will try to interruption things downwardly and shed lite to the key points and the details of the uploading process. Before nosotros get there though, it'southward necessary to have a quick discussion about some ground cognition that nosotros all should have on this topic.

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

Before we kickoff doing actual work, information technology's necessary some important things to exist mentioned starting time. Let me get-go by saying that in order to upload files to a server, multipart/form-data is the content blazon that should be specified in the web request. This content type allows to ship files or large amounts of data in combination with other usual data that should exist posted. "Multipart/course-data" content type tells to HTTP request that posted data should exist cleaved into parts, as if they were to be posted by a spider 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, it's necessary for the server to know where a office 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, so it must exist as much unique as possible. Information technology ever starts with two dashes ("–"), with an capricious combination of other alphanumeric characters coming afterwards. Unremarkably, boundaries showtime 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 it. An attribute called "proper noun" should also be provided in the header, as it specifies the proper noun 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 central-value pairs are enough when calculation single data (meaning no files) to the request's HTTP torso. When appending files data, the filename should be as well included in the "Content-Disposition" header with the original proper name of the file, as well as the content type (MIME blazon) of each file that is about to be uploaded.

The following is a imitation example of a HTTP request body that uses the "multipart/form-information" content type:

Notice how everything mentioned in the previous paragraphs is used. At first, the "multipart/form-data" content type is specified along with the boundary string that separates the data parts. Run across how purlieus indicates the starting time of each part and likewise run across how semicolon (";") separates attributes in headers. Line breaks are also important when building a HTTP body such the above i. In unmarried fields, an empty line exists between the "Content-Disposition" header and the actual field value, while the purlieus of the next function comes correct subsequently in the side by side line. In file parts, the "filename" attribute contains the proper noun of the file, while an boosted empty line exists between the file contents and the adjacent boundary. The torso ending is highlighted by the boundary, plus 2 more dashes equally a suffix to it.

I am encouraging you to take a look at the W3C HTML Specification and read more virtually encoding content types and the "multipart/form-data" peculiarly. You lot don't have to stop there of form; a general search on the spider web will return lots of resources to read about this topic.

Nearly The Demo App

So, as I said in the commencement of this post, we are going to go on edifice on the custom class we created in the previous tutorial, called RestManager. To get started, please download a starter package which contains a Xcode project with that class and i more than directory with a demo server implementation (meet next part). In Xcode project yous will find iii files that we'll utilise 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 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 Terminal. Whatever input values will be difficult-coded. Therefore, we'll entirely focus on the file uploading feature that we'll add to the RestManager class. Obviously, you are free to create any UI you desire if y'all want to create a more than dynamic demo application.

About The Server

Subsequently nosotros end implementing all the new lawmaking we'll encounter in the following parts, we'll demand to examination if file uploading is really working. For that purpose, a unproblematic server implemented in Node.js is included in the starter package that you downloaded; you lot will find it in the Server subdirectory. You tin can keep it in the location that currently is, or copy it anywhere else you lot desire in your deejay.

In order to run the server, yous must have Node.js installed on your figurer. If yous don't, please check here or here on how to do that. Open Last and type the post-obit command:

In that location is a space character after the cd control. And so switch to Finder, and drag and drop the Server directory to terminal and press the Return key:

By doing so, y'all don't have to type the path to the server directory; it's automatically appended to the command in terminal.

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

This command volition bear witness the current directory contents, and if you see something similar to the next ane, then you lot're only fine:

To start the server only blazon:

You should see the bulletin:

Server started successfully on port 3000!

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

Notation: If yous are already running another server at port 3000, edit the index.js file and set a custom port number to the port variable. So restart the server with the node alphabetize.js control.

Requests made to "http" addresses are not immune past default in iOS equally they are considered insecure. Notwithstanding, for the sake of the tutorial, localhost has been whitelisted in the Info.plist file of the starter projection and so you volition come across no trouble in testing the app later.

Representing Files

The first thing we need to take care of is how files are going to be represented in the RestManager form. For whatever file that is about to be uploaded, nosotros need to have the following data bachelor at the time of the HTTP body preparation:

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

Evidently, all that information could be stored in a dictionary, but that wouldn't be the best approach in Swift. To do information technology better, let's create a struct which we'll telephone call FileInfo. Open up the RestManager.swift file in the starter Xcode project, and go to the terminate of it. Yous will find the following empty extension:

This is where we'll add almost all new lawmaking regarding the file uploading feature. Inside this extension, add the following structure:

The four properties will keep the data described earlier. As yous will see later, if any of the above backdrop is nil the file won't be added to the HTTP body for submission to the server.

We can make the initialization of a FileInfo object more friendly if nosotros add the post-obit 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 will be plenty. File contents will be read in the higher up initializer.

Creating The Boundary

Having a solution on our hands almost how to correspond files, allow'south create a method which will exist responsible of creating the boundary cord. Remember that a boundary must be unique and definitely not an ordinary cord that could be potentially found in the actual data that will be uploaded. As I said in the beginning of the postal service, even though boundaries get-go with 2 dashes ("–"), they normally have several more than dashes following and a random alphanumeric string at the end. That's not mandatory, but it's the logic nosotros will follow hither.

Right afterward the FileInfo struct, ascertain the following private method:

I volition show you two different ways to generate the random purlieus string.

Using A UUID String

The fastest way to go a random cord is to generate a UUID value:

The above will generate something similar to this:

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

The original UUID will now expect like this:

Let's construct the purlieus cord. It will be a concatenation of 20 dashes at the kickoff and the transformed UUID value:

If you like exaggerating, add together the current timestamp to the finish equally well:

A boundary string created with the higher up will look like:

Well, that looks quite unique and random, no?

Here's the implementation of the entire method:

Using Random Characters

Equally an culling to the above we can create a mechanism which will selection random characters from a drove of available characters, and using them to form a string which will be appended to the boundary string. The collection of available characters will be parted past all messages ranging from upper cased "A" to "Z", lower cased "a" to "z", and all digits from "0" to "ix".

Nosotros won't really need to hard-code anything, every bit we can programmatically construct everything. We will be based on the ASCII table for that.

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

The above is equivalent to this:

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

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

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

Now, permit'south join all these ranges into a collection, or in other words a sequence of ranges (closed ranges more peculiarly) with aim to go the actual characters subsequently:

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

Even though information technology's non obvious unless someone looks up for information technology, the above tin can exist easily converted into a String value:

Information struct provides several initializers for creating a information object and there is i amid them that accepts a sequence as an statement, exactly as we practice in the Information(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 cord of 20 random characters now:

At kickoff we initialize a string value called randomString. And so, nosotros create a loop that will 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 (Cord(toString.randomElement()!)). This new String value is appended to the randomString.

Note that is safe to force unwrap the value of the randomElement() method, as information technology returns nix 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:

Utilize the implementation you prefer the most. The second 1 is more than "Swifty" but it requires a flake of more code. At the stop of the mean solar day, both approaches are going to piece of work equally well.

An important notation: I've mentioned already that the boundary string which separates the parts of a multipart body starts with two dashes ("–"). These 2 dashes are not included in the dashes of the boundary string we generated in both approaches here. This cord volition exist provided as-is to the request as a asking header along with the content type and server will try to locate it afterward the ii dashes prefix. Likewise, a purlieus string tin exist with no dashes at all; we just add them to minimize the possibility to discover similar cord in the uploaded information. As you volition meet later, the two dashes prefix will exist manually appended whenever necessary.

Extending Information Construction

Our next steps involve the preparation of the HTTP body using any arbitrary data provided to the class, as well equally using the files data. But before we become into that, we will extend the Data structure and we volition create the following generic method:

The purpose of this method is to let us hands suspend the values of the values drove to the data object that calls it. And as you'll encounter, nosotros'll exist interested for String and Data types only.

Just for description, nosotros could avoid implementing this method. Yet, the code that we will add to information technology would have to be repeated multiple times in different points in the RestManager class, and that definitely would not be a wise move.

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

Add the new method'south definition in it:

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

Next, we'll distinguish the type of the given values. Permit's first with the String type. In this instance, we'll make a loop to access all values in the values parameter collection:

In each repetition nosotros volition convert the string value into a Information 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, nosotros'll set the status flag to false and we'll suspension the loop.

We volition follow a quite like approach in case of Data input values. Of course, there is no need to initialize whatsoever new Information object or make a conversion of any type. Nosotros are appending one data value to another:

Lastly, let'due south indicate that nosotros don't intendance most whatever other type of values:

Next, we'll bank check the status value. If it'due south truthful, then we tin suspend the newData local variable to the self object (the Information object that is used to call this method).

At the terminate, nosotros should not forget to return the status equally the result of the method:

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

Creating the HTTP Body

In the current implementation of RestManager there is a method named getHttpBody(). Its purpose is to prepare the HTTP body with the information that volition be posted to the server. Although this method works cracking in any other example, unfortunately information technology's non of much help in case of file uploading. There is the boundary string we have to accept into account, also as the special headers and formatting required when using the "multipart/grade-data" content type. To serve our new needs, we'll implement a similarly named method which will exist accepting the boundary string equally an argument (also known as method overloading).

In the new extension of the RestManager class, right below the createBoundary method, add the following:

Proceed in mind that the HTTP torso must be a Data value, then we 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 be posted to the server except for files. That's the data that would be normally submitted if at that place were no files to upload at the same time, and it'southward kept in the httpBodyParameters property (as a reminder, httpBodyParameters is a property in the RestManager class and it's of RestEntity type, a custom construction – find information technology in RestManager and read more in the previous tutorial most it).

httpBodyParameters has a method called allValues() and returns all data equally a dictionary (a [Cord: Cord] dictionary). We'll use it to access all values that should be sent to the server and suspend them to the body variable. Right subsequently the var body = Information() line add the following:

A small stop here at present as we have to talk over what exactly we'll exist appending to the body. Let's run into once more function of the case presented in the first of this post:

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

  • At offset there is the boundary string, and right afterward that a line break. In HTTP headers, a line interruption is marked with "\r\north" (carriage return and new line character), not just the "\due north" that nosotros are mostly used to. Programmatically, this could be written like: "--\(boundary)\r\n" (see the ii dashes before the boundary string).
  • Next, there is the "Content-Disposition" header with the name attribute only in information technology. Header is followed by a line interruption two times. We could write this like so: "Content-Disposition: class-information; name=\"\(fundamental)\"\r\n\r\north".
  • Lastly, it'southward the actual value followed by a line interruption. That'south easy: "\(value)\r\north".

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

We will use for first time the append(values:) custom method we implemented in the previous step in order to catechumen these strings into Information objects and append them to the body variable:

And that's the terminal thing we had to do in this method. Let'south run into it birthday now:

We'll utilise the results of this method in a while. For at present, nosotros accept to add together the files information to the HTTP torso as well.

Adding Files To HTTP Body

One could say that the getHttpBody(withBoundary:) method we just implemented along with the new i we will implement hither consist of the most important function of the overall work we have to practice in order to make file uploading possible. And that would be pretty much true, as we've congenital all the helper methods we demand and now we are dealing with the core functionality.

And so, standing on edifice the HTTP body, let's ascertain the following new method:

Allow's talk first about the parameters. The first ane is a collection of FileInfo objects, and it contains the data for all files that are about to exist uploaded. The 2d parameter value is the data object that represents the HTTP body. Whatever changes that volition exist made to that object inside this method volition be reflected out of it also because it'southward marked with the inout keyword. The last parameter is the boundary string, every bit we necessarily need it to divide data parts.

Y'all 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 proceed their names into an array, which in turn the method will return. In normal conditions this method should return nil, meaning that information from all files was successfully appended to the HTTP body information.

Let's beginning adding some lawmaking, with the first one being the following local variables:

status volition indicate whether all pieces of data for each single file in the files drove were successfully combined in one Data object, which tin be so appended to the body inout parameter. If status is false, we'll be appending the proper name of the matching file to the failedFilenames array.

Let'south start a loop now:

The first thing we have to do is to make sure that all properties of each file object have actual values and then we can go along:

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

Now, let's come across once again the instance presented in the beginning of the tutorial so we understand what we accept to do:

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

  • At get-go there is the boundary with the line break at the end. We already know how to write that in code.
  • Next, nosotros have the "Content-Disposition" header. The addition here (comparison 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: form-information; name=\"\(proper name)\"; filename=\"\(filename)\"\r\due north".
  • Right later on we have the content blazon of the file. See all the available MIME Media Types. In code this is like so: "Content-Type: \(mimetype)\r\n\r\n".

Permit's make a interruption here and allow's suspend all the above to an array:

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

Permit's continue where we had stopped from. The next particular in a file part is the bodily file contents. Remember that file contents are represented by the fileContents property in a FileInfo object, which is a Data object. And so far we were dealing with strings only. File contents must be appended to the data variable also:

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

Lastly, notice in the above case that there is an empty line right after the file contents which should be added to the information also:

These iii atmospheric condition we wrote must be embedded into each other. If all of them are truthful, then all data pieces for the current file were successfully added to the data object, and we'll bespeak that by making the status truthful:

Run across 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 utilise information technology again and again.

Next, allow'due south cheque the status value for each file. While nevertheless being on the loop:

If status is truthful, we append the data variable to the torso which represents the HTTP torso. If not, then nosotros initialize the failedFilenames array in example it's not initialized already, and we go along the proper name of the current file in it.

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

Our new method should at present look like this:

Endmost The HTTP Trunk

Now that nosotros created methods which build the HTTP body by appending whatever mail information and file data, we must create one more than which will close the body. Remember that in "multipart/course-information" the HTTP body closing is marked by the purlieus string and two dashes as a suffix to it:

As you can guess, doing and then doesn't require much of work as all it takes is this:

For one more than time here the body parameter is marked every bit inout, so the information statement will be passed by reference and the changes made to it within this method will become visible to the caller too. Besides that, notice the line breaks earlier and after the closing cord which ensure that the closing boundary volition exist the only content in the line.

It's actually important not to forget to call this method and indicate the end of parts in the multipart HTTP trunk.

Uploading Files

It's most time to put everything together and make file uploading possible. The method we'll write here will exist public, so you can go and add it to the top of the form along with other two public methods existing already. Hither is its definition:

In accord to what we did to the other two existing public methods, we are going to perform all actions in this method asynchronously. We won't run annihilation on the chief thread since file uploading could accept pregnant corporeality of fourth dimension and nosotros don't want apps to show frozen. In code that ways:

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

The first actual action we have to take is to add together whatever 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'south call the createBoundary() method we implemented today and let's create the boundary string:

Notice that since cocky is used as an optional, purlieus becomes an optional value also, regardless of the fact that createBoundary() does non return an optional. Then, in instance at that place'south no purlieus string to continue, nosotros telephone call the completion handler passing the fault shown above and we render from the method. This custom error doesn't be yet in the course, we'll add it in a while.

Allow'southward get going, and in the next step allow's add the "multipart/form-information" along with the purlieus string to the collection of the request headers:

To refresh your memory, requestHttpHeaders is a RestEntity property which keeps all HTTP request headers as fundamental-value pairs. It's important to highlight that since we specify the content type header hither, there is no need to provide a content blazon header manually while preparing the asking. Not only it'due south redundant, it's as well dangerous as it could create conflicts and make the server reject the request.

Side by side, permit's starting time preparing the HTTP body. Nosotros'll start past calling the getHttpBody(withBoundary:) method:

Once once again, since cocky is an optional, body might be nil in case cocky is zero. So, in that case we telephone call the completion handler with some other custom error and nosotros return from the method.

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

failedFilenames is either nil 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 trunk.

We should non forget to close the HTTP trunk properly:

Nosotros are set now to create the URL request:

The method we use hither is already implemented in the RestManager form and we discussed about it in the previous tutorial. Notice that we laissez passer the URL with any potential query items (targetURL) and the HTTP torso as arguments.

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

The upload method is now ready:

There is one concluding matter to do earlier nosotros exam out everything. To add the 2 new custom errors to the CustomError enum. Notice it in the RestManager class and update it as shown next:

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

That's it! Time to upload files!

Testing File Uploading

The fourth dimension to examination 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 simply, and here we will prepare the FileInfo object that will contain its data.

Before we proceed, let me remind you lot that in the starter Xcode projection yous downloaded in that location are iii files for testing: "sampleText.txt", "samplePDF.txt" and "sampleImage.pdf". We'll utilise the "sampleText.txt" hither, but experience gratuitous to modify and use any other file yous desire. Sample files be in the application'due south bundle just for making the instance equally simple equally possible, just in real apps the you lot'll nigh always fetch them from the documents directory.

So, let'south beginning by 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 style and you adopt to manually set all values including the files contents, here's your alternative:

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

The URL where nosotros'll make the request to upload the file is: http://localhost:3000/upload. We volition pass a URL object forth with an array that volition incorporate the fileInfo object equally arguments to a new method (we'll implement it right side by side):

upload(files:toURL:) is a small method responsible for making the request every bit you lot can see next. We could have put that lawmaking in the uploadSingleFile() method, merely we'll use information technology again in a while when we'll upload multiple files. So, nosotros'd better avoid repeating code.

In the completion handler we don't do anything detail. We just print the HTTP status lawmaking, nosotros brandish any potential errors, and the server's response after nosotros convert information technology from JSON to a dictionary object. Of form, we besides print the list of failed to be uploaded files (in case there is any).

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

Run the app now and expect at both in Xcode panel and in the terminal where the server'south output is printed. If you lot followed everything pace by step up until hither, y'all should get this in Xcode:

At the same time, in terminal y'all should have the details of the uploaded file:

I wanted to make the pocket-sized demo server and the file uploading process bear as much naturally as possible, then files sent to this server implementation are actually… being uploaded! In Finder, go to the Server directory that you lot 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 past also sending additional data forth with the asking. Right subsequently the initialization of the FileInfo object in the uploadSingleFile() method add the following two lines:

Run the app again. In the last y'all should run across the additional uploaded data also:

Let'southward upload multiple files now. Nosotros'll do that by creating a new method similar to the previous one, with the difference existence that instead of initializing one FileInfo object, we'll initialize iii of them so we tin upload all sample files nosotros take. Here it is:

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

This time you should come across the names of the uploaded files in terminal:

Note that the current server implementation supports up to ten simultaneous files to exist uploaded. Of course you lot are free to modify 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 characteristic, nosotros take managed to build a modest and lightweight form capable of covering our needs in making web requests. "Multipart/form-data" content type and the way HTTP body is built tin can be sometimes confusing, but if y'all break things downwardly then everything gets like shooting fish in a barrel. I hope what I shared with you here today to exist of some value, and I wish you are enjoying RESTful services even more now. You are always welcome to add more than features or adjust the current implementation co-ordinate to your needs. See you side by side time!

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

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

Posted by: alleyarerest.blogspot.com

0 Response to "What Data Takes When Upload Image To Post Api Call"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel