don't stop believing

이미지 업로드와 static file (이미지, html) 보기 본문

Swift/Perfect

이미지 업로드와 static file (이미지, html) 보기

Tongchun 2018. 2. 7. 17:14

Perfect로 이미지를 업로드하고 이미지를 확인해 보겠습니다.

언제나 그렇듯이 swift 버전을 확인하고 가겠습니다.

1
2
3
$ swift -version
Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
Target: x86_64-apple-macosx10.9
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

프로젝트 폴더를 만들고 이동합니다. 그리고 swift package init으로 초기화 합니다.

1
2
3
4
5
6
7
8
9
10
$ mkdir nGleServer014
$ cd nGleServer014
TongChunui-MacBook-Pro:nGleServer014 tongchunkim$ swift package init --type executable
Creating executable package: nGleServer014
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/nGleServer014/main.swift
Creating Tests/
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcodeproj 파일을 생성하고 열어줍니다.

1
2
$ swift package generate-xcodeproj
$ open nGleServer014.xcodeproj/
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcode가 실행되면 Package.swift 파일에 PerfectHTTPServer를 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "nGleServer014",
dependencies: [
.package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", from: "3.0.0"),
],
targets: [
.target(
name: "nGleServer014",
dependencies: ["PerfectHTTPServer",]),
]
)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

 xcode를 닫고 swift package update와 swift build를 해줍니다.

1
2
$ swift package update
$ swift build
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

그리고 webroot 폴더를 만들고 그 안에 업로드한 이미지를 받을 images와 html 파일을 담을 docs 폴더를 만들어 줍니다.

그리고 main.swift가 있는 위치에 controller.swift 파일을 만들어 줍니다. 서버 실행과 route를 분산해 줄겁니다.

1
2
3
4
5
$ mkdir webroot
$ cd webroot/
$ mkdir images docs
$ cd ../Sources/nGleServer014/
$ touch controller.swift
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

전체 폴더의 tree 구조는 아래와 같이 만들어 집니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ tree
.
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
·· └── nGleServer014
·· ├── controller.swift
·· └── main.swift
├── Tests
├── nGleServer014.xcodeproj
·· ├── COpenSSL_Info.plist
·· ├── PerfectCHTTPParser_Info.plist
·· ├── PerfectCZlib_Info.plist
·· ├── PerfectCrypto_Info.plist
·· ├── PerfectHTTPServer_Info.plist
·· ├── PerfectHTTP_Info.plist
·· ├── PerfectLib_Info.plist
·· ├── PerfectNet_Info.plist
·· ├── PerfectThread_Info.plist
·· ├── project.pbxproj
·· ├── project.xcworkspace
···· ├── contents.xcworkspacedata
···· └── xcuserdata
···· └── tongchunkim.xcuserdatad
···· └── UserInterfaceState.xcuserstate
·· ├── xcshareddata
···· └── xcschemes
···· ├── nGleServer014-Package.xcscheme
···· └── xcschememanagement.plist
·· └── xcuserdata
·· └── tongchunkim.xcuserdatad
·· └── xcschemes
·· ├── nGleServer014.xcscheme
·· └── xcschememanagement.plist
└── webroot
├── docs
└── images
└── dejavu_1517991366.jpg
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 다시 xcodeproj를 만들어 주고 열어 봅니다.

1
2
$ swift package generate-xcodeproj
$ open nGleServer014.xcodeproj/
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

먼저 main.swift로 이동합니다.

Controller 클래스는 아직 안만들었지만 우선 아래와 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
let server = HTTPServer()
server.serverPort = 8080
let control = Controller()
server.addRoutes(Routes(control.routes))
do {
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 controller.swift로 이동합니다.

import먼저 해줍니다.

1
2
3
4
import Foundation
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 request로 들어오는 json 데이터 key 정보들을 정의합니다.

json 데이터가 많아지면 계속 추가하면 됩니다.

1
2
3
4
5
struct JSONKey {
static let userName = "user_name"
static let image = "image"
static let imagePath = "image_path"
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 Controller 클레스를 만들고 routes를 정의합니다.

이미지를 업로드할 uri와 이미지를 볼 uri를 추가하고 그에 맞는 handler를 추가합니다. handler 함수는 아래에 설명됩니다.

1
2
3
4
5
6
7
8
9
final class Controller {
var routes: [Route] {
return [
Route(method: .post, uri: "/upload", handler: uploadImage),
Route(method: .get, uri: "/image/**", handler: viewImage),
]
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

request로 전달되는 데이터 중 필수로 지정된 key가 모두 있는지 확인하는 함수를 만들어 줍니다.

필수로 지정된 key가 모두 있다면 nil을 반환하고 부족하다면 Array로 반환됩니다. 이후 옵셔널 체인(if let)으로 처리합니다.

1
2
3
4
5
6
7
8
9
10
11
12
func lakedParams(_ paramsNeeded: [String], _ paramsReceived: [(String, String)]) -> [String]? {
var laked: [String] = []
for param in paramsNeeded {
if paramsReceived.filter({ (key, _) -> Bool in
return key == param
}).isEmpty {
laked.append(param)
}
}
return laked.count > 0 ? laked : nil
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이미지 저장 디렉토리를 불러옵니다.

이미지가 저장될 폴더는 ./webroot/images 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let imageDir: Dir? = {
let imageDir = Dir("./webroot/images")
if imageDir.exists == false {
do {
try imageDir.create()
print("Working Directory (\(imageDir.path)) for examples created.")
} catch {
print("Could not create Working Directory for examples.")
return nil
}
}
return imageDir
}()
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

response로 전달할 함수를 만들어 줍니다.

1
2
3
4
5
6
7
8
9
10
func returnMessage(result: Int, data: [String: Any], response: HTTPResponse) {
do {
try response.setBody(json: ["result": result, "data": data, "ts": Date().ticks])
.setHeader(.contentType, value: "application/json")
.completed()
} catch {
response.setBody(string: "Error handling request: \(error)")
.completed(status: .internalServerError)
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

timestamp를 만들기 위해 extension Date도 해줍니다.

1
2
3
4
5
extension Date {
var ticks: UInt64 {
return UInt64((self.timeIntervalSince1970 + 62_135_596_800) * 10_000_000)
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

드디어 upload 함수를 만들 차례입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
func uploadImage(request: HTTPRequest, response: HTTPResponse) {
// .
let needParams = [JSONKey.userName]
if let lakedParams = lakedParams(needParams, request.postParams) {
let returnData = ["error": " key . \(lakedParams)"] as [String: Any]
returnMessage(result: 1001, data: returnData, response: response)
return
}
//
guard let imageInformation: MimeReader.BodySpec = request.postFileUploads?.first,
let imageFile = imageInformation.file else {
let returnData = ["error": " ."] as [String: Any]
returnMessage(result: 1002, data: returnData, response: response)
return
}
//
guard let userName = request.param(name: JSONKey.userName) else {
let returnData = ["error": " ."] as [String: Any]
returnMessage(result: 1003, data: returnData, response: response)
return
}
//
guard let imageDirectory = imageDir else {
let returnData = ["error": " ."] as [String: Any]
returnMessage(result: 1004, data: returnData, response: response)
return
}
//
let timestamp: Int = icuDateToSeconds(getNow())
// _.jpg
let imageFileName: String = userName + "_" + String(timestamp) + ".jpg"
//
let imageFilePath: String = imageDirectory.path + imageFileName
//
do {
try imageFile.copyTo(path: imageFilePath, overWrite: false)
} catch {
response.completed(status: .internalServerError)
return
}
let imageUrl = "http://" + server.serverAddress + ":\(server.serverPort)" + "/image/" + imageFileName
let returnData = [JSONKey.userName: userName, JSONKey.imageUrl: imageUrl]
returnMessage(result: 0, data: returnData, response: response)
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 Postman으로 데이터를 전달해 봅시다.

response 데이터가 잘 옵니다.

xcode에서 webroot/images 폴더에 이미지가 업로드 되는지도 확인해 줍니다.

여러번 테스트했는데 모두 잘 들어오고 있습니다.


이제 이미지를 불러 봅시다.

이미지를 보기위해 위에서 route에 get method를 추가했었습니다.

Route(method: .get, uri: "/image/**", handler: viewImage),

viewImage 함수는 아래와 같이 작성합니다.

1
2
3
4
5
func viewImage(request: HTTPRequest, response: HTTPResponse) {
request.path = request.urlVariables[routeTrailingWildcardKey]!
let handler = StaticFileHandler(documentRoot: "webroot/images", allowResponseFilters: true)
handler.handleRequest(request: request, response: response)
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이미지를 upload하고 response로 받은 데이터의 image_url 값을 브라우저로 불러 봅니다.

http://0.0.0.0:8080/image/dejavu_1517993432.jpg

viewImage() 함수는 이미지나 html처럼 static한 데이터를 보는데 사용할 수 있습니다.


여기까지 입니다.





Comments