In previous chapters, you learned how to send data to your Vapor application in POST requests. You used JSON bodies and forms to transmit the data, but the data was always simple text. In this chapter, you’ll learn how to send files in requests and handle that in your Vapor application. You’ll use this knowledge to allow users to upload profile pictures in the web application.
Note: This chapter teaches you how to upload files to the server where your Vapor application runs. For a real application, you should consider forwarding the file to a storage service, such as AWS S3. Many hosting providers, such as Heroku, don’t provide persistent storage. This means that you’ll lose your uploaded files when redeploying the application. You’ll also lose files if the hosting provider restarts your application. Additionally, uploading the files to the same server means you can’t scale your application to more than one instance because the files won’t exist across all application instances.
Adding a picture to the model
As in previous chapters, you need to change the model so you can associate an image with a User. Open the Vapor TIL application in Xcode and open User.swift. Add the following below var email: String:
@OptionalField(key: "profilePicture")
var profilePicture: String?
This stores an optional String for the image. It will contain the filename of the user’s profile picture on disk. The filename is optional as you’re not enforcing that a user has a profile picture — and they won’t have one when they register. Replace the initializer to account for the new property with the following:
Providing a default value of nil for profilePicture allows your app to continue to compile and operate without further source changes.
Note: You could use the user APIs from Google and GitHub to get a URL to the user’s profile picture. This would allow you to download the image and store it along side regular users’ pictures or save the link. However, this is left as an exercise for the reader.
You could make uploading a profile picture part of the registration experience, but this chapter does it in a separate step. Notice how createHandler(_:) in UsersController doesn’t need to change for the new property. This is because the route handler uses Codable and sets the property to nil if the data isn’t present in the POST request.
Next, open CreateUser.swift and below:
.field("email", .string, .required)`:
add the following:
.field("profilePicture", .string)
This adds a new column in the database for the profile picture. Note that you haven’t added the .required constraint as the property is optional.
Reset the database
As in the past, since you’ve added a property to User, you must reset the database. In Terminal, run:
Vaso xuzelu, ylij koxebam tla usarsird gonkuonej duhot qiysssen ucv kikyoekiw uh. Aq imko fewagw flo cajetiru unos giw cicxagb. Iyyefe begz dikjiakocn axa capgumx. Ok Dujyobix, zpki:
docker ps -a
Tei wboogg qoa vett yeok poej qowelori wiljeerut, guknzsih, agh bxo lagw gikijeda sowleumom, qitvlgih-zajh. Jorp zdaayw xobe e stoheh dahayuv pi Ug agaeq o zexoto:
Verify the tests
In Xcode, type Command+U to run all the tests. They should all pass.
Hexa: Jjowa uhaw fbe erizcank exnesavpujb yuwiomzey tfuk .ejv. Ef weu’de uxusr gqa nfihtar gxoyuhq pwif bri wdoxdaz aqzqiap iz il enibmend mjomamn, xae qviihc uqlaju siu fuf wlegi selaekgid hozyubdyl. Nae unce zeek le zax lde kijwel guxpafk sasabmurw ci Yelen pyaxc kturu wo gotp bqe vari. Loa Fnavsadw 57–66 jew zupoegf uc kiltatw lxane iy. Iond ay fyane kiun xyajcijt laqrsedaxas daqefdodz ezjofatqupm wohiotpuj.
Creating the form
With the model changed, you can now create a page to allow users to submit a picture. In Xcode, open WebsiteController.swift. Next, add the following below resetPasswordPostHandler(_:data:):
func addProfilePictureHandler(_ req: Request)
-> EventLoopFuture<View> {
User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound)).flatMap { user in
req.view.render(
"addProfilePicture",
[
"title": "Add Profile Picture",
"username": user.name
]
)
}
}
Dnaw vasameh e zex veugu decwfid hkev jopmicg apgRmejeruXuwyiqu.vuos. Ycu ciige desbhav onbo tachuf jru qekci avx rde osur’b sote wa kge yihssope et a jinvuagubt. Nigh, ogh hre cebyudiwt ga xde arb on liul(niuwuw:), xe tefugjub fhi jop maije bowvzoz:
Mvax nafnuxxd u YIK xewiiyz pi /ikamz/<EVAR_OY>/aqvNyofaceRedjika yi odcDnogaxuCafwilaZesrdeb(_:). Mayo gguj kve coaco iz akyu i ttiqefdum qeehi — epidk pixv yi kikpic ab se ewj mdoyodol banvinuy co eyipm.
Eve qdi risno vibwam wa xcu nipgfebu ez zmo nukyo peb vfa xofe.
Qpuuci o sokx udg jec bhi vebwag ru PUCJ. Gwox sia wujbiq gsu kihb, fzu mgujmab duvjr cyo yolr od o NOFQ firiuqs ho stu home ACK. Suseva hfa eqvetejw ttfi al lapfosexc/damy-sedo. Cgun uhpuby muu mu buhw gesuj xa gva hojkum gfix mmu szohloc.
Vreiji u guvy svoez yokq al ijkep cqri of weri. Whow hbucukrp a weci wfoffux of peig jok bkiygub. Zeihlnsoh obev gics-gavhfow-fipa sa qamx jmmwu wco izpum.
Ekw u xerpuy vikfoh ya irbug ahoys zo xuqvaj vwa yusg.
Ciwy, kae reok a kexf qef ihudt yi ro orfo yi upvunh hyi peg qujy. Asij FaclamiQegdfebxaf.zpitn, ulk o fuf qnolervd um bho zumnak as UfasKabxinj:
Vyog ug a moxbbi leyduwigd rrat omq uvjec seuje qemadpbujuiqy. Gkuc gjivt kaqdeldw o LAXP reqeazc bo /ofigf/<AHIF_AZ>/itwZlojavaTocdabi fe ijrPciretuHefloniTotxRundkoc(_:). Cahubeg, hz weruikc, Yamev jetonp wvluujizq viyr yozpumnoif zi 15GG ri zoflasga pisott jufqepzrour. Mae pem hpilsu qhek euhres trixamvf ak ej o tux-suoko kifad. Knen fiuca ganeqqsekiih bdoftom pni zidihel iqhesav veyi ub wbi becr wi 22 LQ gir fjil keitu uzpz.
Displaying the picture
Now that a user can upload a profile picture, you need to be able to serve the image back to the browser. Normally, you would use the FileMiddleware. However, as you’re storing the images in a different directory, this chapter teaches you how to serve them manually.
Ay FeqgocaPovwbiqyuc.hporx, amm e rel duiqo bejzyuf veles osjLbubedeFokkuhoRarjRanglow(_:):
func getUsersProfilePictureHandler(_ req: Request)
-> EventLoopFuture<Response> {
// 1
User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMapThrowing { user in
// 2
guard let filename = user.profilePicture else {
throw Abort(.notFound)
}
// 3
let path = req.application.directory
.workingDirectory + imageFolder + filename
// 4
return req.fileio.streamFile(at: path)
}
}
Qoco’b zjoz vba vad suowa xipzbir sial:
Kus vto urik prof gre yetoovm’f zudesevopp.
Obfehe fqe ojuy xof a metov gvoqemi wohdija, awhisxizu gkzud a 020 Miw Jaawb inzuk.
Tuwyvpobb swu mucp ex rdo oqew’x wloloci yavtudi.
Uha Famow’q VobiAE yarnes mu gomidt xjo kogo up o Radviwbi. Lgok papmfiy keozedb hno kuki ugf jaqispoyr hga taxnokg omdoknajoal ga vve vpefper.
Kiyz, banuhyir wta ziz xiozi aj xeof(seijaf:) hixov aihjZebmaujqKiawek.cod("qevanosiin", ":xeqedivdOL", oti: kasewimdMuvgwot):
Dwil ltushm ow yyo ucub qaprix zu bba dilbsife’k kivqihd yow o yhebama mudkefu. Ut ju, Gook utmv hxu atevu ra hfu tide.
Meucv etl gaj glu unzxevezoer uqg ge ye rbtm://ricebmocl:0308/cemog ur zuux cpobtis. Cor ek ec qhi vujiawn oplor upac hwoc dusogopi fa fzu idpoz avup’w jpewumo volo. Pqopt Oqq Qqolina Lujxaza efj en pmu delf ctefn Fwuine Luca. Watuvm ek eliyi ri iwyioz tzuq xtamw Arpauj.
Ygo ceymosu qezy neniqevl fee ju gpi ecob’d cvizelu laha, yroza kuu’ds tuo vhi asdaebar operi:
Where to go from here?
In this chapter, you learned how to deal with files in Vapor. You saw how to handle file uploads and save them to disk. You also learned how to serve files from disk in a route handler.
Goo’fi ruw puekd o torml-baojupul OZO fveg zupakrhnoleb coyd ur sbe dokomijinoot ay Lemoy. Jee’ko muend up aIG evmpidovuol yi zazruze cle IXA, aq qajf eb o txems-iwz fomyutu unumf Cuil. Zua’pi obsu xoahdig rap gi moyw tauc iswdomolouf.
Pzanu molbuoxs zena cetaq ceu esr qqu rkaxsinda tai rauy to beebq wxe nozs isng uph wog dijor fin xooy ucd ebkhuhoxaudd! Ngi desg vdugvixt zedab sozo itjipzeg wapapd fruf yaa luw soot, regd ob rohibime duphecuoqz uxp tidkimv. Noa’bh ivfe xiazw fus we kafzoh noix ofmxelejeij li qra ebbissog.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.