Когда я почти закончил статью, я заметил, что веб-розетки также поддерживаются в Play 2.0. Мне очень нравится разработка в Play и в Scala, поэтому в качестве эксперимента я переписал внутреннюю часть стека Jetty / Java / JavaCV в стек Play2.0 / Scala / JavaCV. Если вы хотите сделать это для себя, убедитесь, что вы начинаете с кода внешнего интерфейса здесь . Так как код внешнего интерфейса не изменился, кроме места, где прослушиваются веб-сокеты.
Настройка среды Play 2.0
Я не буду много говорить о том, как начать проект Play 2.0 / Scala. Вы можете найти подробности в некоторых других моих постах, если вам потребуется дополнительная информация. Нам нужно настроить зависимости для JavaCV, чтобы их можно было использовать из Play 2. Я вручную добавил их в свой локальный репозиторий ivy, чтобы я мог ссылаться на них из конфигурации sbt, как и любая другая зависимость. Для моего примера я создал следующую структуру каталогов для библиотек JavaCV:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
. /play-2 .0-RC2 /repository/cache/javacv . /play-2 .0-RC2 /repository/cache/javacv/javacpp . /play-2 .0-RC2 /repository/cache/javacv/javacpp/ivy-2 .3.1.xml . /play-2 .0-RC2 /repository/cache/javacv/javacpp/ivydata-2 .3.1.properties . /play-2 .0-RC2 /repository/cache/javacv/javacv . /play-2 .0-RC2 /repository/cache/javacv/javacv/ivy-2 .3.1.xml . /play-2 .0-RC2 /repository/cache/javacv/javacv/ivydata-2 .3.1.properties . /play-2 .0-RC2 /repository/cache/javacv/javacv-macosx-x86_64 . /play-2 .0-RC2 /repository/cache/javacv/javacv-macosx-x86_64/ivy-2 .3.1.xml . /play-2 .0-RC2 /repository/cache/javacv/javacv-macosx-x86_64/ivydata-2 .3.1.properties . /play-2 .0-RC2 /repository/local/javacv . /play-2 .0-RC2 /repository/local/javacv/javacpp . /play-2 .0-RC2 /repository/local/javacv/javacpp/2 .3.1 . /play-2 .0-RC2 /repository/local/javacv/javacpp/2 .3.1 /ivys . /play-2 .0-RC2 /repository/local/javacv/javacpp/2 .3.1 /ivys/ivy .xml . /play-2 .0-RC2 /repository/local/javacv/javacpp/2 .3.1 /jars . /play-2 .0-RC2 /repository/local/javacv/javacpp/2 .3.1 /jars/javacpp .jar . /play-2 .0-RC2 /repository/local/javacv/javacv . /play-2 .0-RC2 /repository/local/javacv/javacv/2 .3.1 . /play-2 .0-RC2 /repository/local/javacv/javacv/2 .3.1 /ivys . /play-2 .0-RC2 /repository/local/javacv/javacv/2 .3.1 /ivys/ivy .xml . /play-2 .0-RC2 /repository/local/javacv/javacv/2 .3.1 /jars . /play-2 .0-RC2 /repository/local/javacv/javacv/2 .3.1 /jars/javacv .jar . /play-2 .0-RC2 /repository/local/javacv/javacv-macosx-x86_64 . /play-2 .0-RC2 /repository/local/javacv/javacv-macosx-x86_64/2 .3.1 . /play-2 .0-RC2 /repository/local/javacv/javacv-macosx-x86_64/2 .3.1 /ivys . /play-2 .0-RC2 /repository/local/javacv/javacv-macosx-x86_64/2 .3.1 /ivys/ivy .xml . /play-2 .0-RC2 /repository/local/javacv/javacv-macosx-x86_64/2 .3.1 /jars . /play-2 .0-RC2 /repository/local/javacv/javacv-macosx-x86_64/2 .3.1 /jars/javacv-macosx-x86_64 .jar |
Как вы можете видеть из этого списка, я только что добавил три javacv-файла в мой локальный репозиторий. Я также добавил минимальный файл ivy.xml, чтобы его можно было использовать из ivy и sbt. Этот минимальный ivy.xml выглядит так:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<? xml version = "1.0" encoding = "UTF-8" ?> < info organisation = "javacv" module = "javacv-macosx-x86_64" revision = "2.3.1" status = "release" > </ info > < publications > < artifact type = "jar" /> </ publications > </ ivy-module > |
С этими файлами, добавленными в мой репозиторий, я могу установить зависимости для проекта Play 2.0 в файле Build.scala.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
import sbt. _ import Keys. _ import PlayProject. _ object ApplicationBuild extends Build { val appName = "PlayWebsocketJavaCV" val appVersion = "1.0-SNAPSHOT" val appDependencies = Seq( "javacv" % "javacv" % "2.3.1" , "javacv" % "javacpp" % "2.3.1" , "javacv" % "javacv-macosx-x86_64" % "2.3.1" ) val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings( // Add your own project settings here ) } |
Теперь запустите «play update» и «play eclipsify», чтобы обновить зависимости и конфигурацию Eclipse (если вы используете Eclipse).
Настройка веб-сокетов в Play
Использование веб-сокетов в Play 2.0 очень просто. Первое, что вам нужно сделать, это добавить URL к вашей конфигурации маршрутов в каталоге conf.
1
|
GET /wsrequest controllers.Application.wsrequest |
И, конечно же, вам нужно выполнить действие, на которое указывает этот маршрут:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
/** * Simple websocket listener configured in play 2. This uses a synchronous model, where * the same channel is used to send the response. For this usecase this is useful, if * we want async processing we could have used Akka actors together with play 2.0 async * support. */ def wsrequest = WebSocket.using[Array[Byte]] { request = > // Create the outbound value that is called for each val out = Enumerator.imperative[Array[Byte]](); val in = Iteratee.foreach[Array[Byte]](content = > { out.push(FaceDetect.detect(content)); }) // tie the in and out values to each other (in, out) } |
В этом коде мы настраиваем входной канал (вход) и выходной канал (выход) и подключаем их к сокету. Всякий раз, когда клиент HTML5 отправляет запрос через веб-сокет, вызывается наш метод «in», и когда мы хотим что-то отправить клиенту, мы можем использовать канал «out». Канал «in» должен быть определен как Iteratee (более подробную информацию см. В этих документах Play). Это означает, что для каждого входного сообщения, которое мы получаем, мы запускаем метод specifici. В этом случае мы запускаем операцию FaceDetect.detect (подробнее об этом позже), и результат этой операции передается обратно клиенту через канал «out». Этот «выходной» канал сам по себе определен как Enumerator (см. Эти документы для воспроизведения). Мы можем присоединить разных слушателей, если хотим к этому перечислителю, но в этом случае мы ничего не делаем с сообщением, просто передаем его клиенту.
Использование JavaCV из Scala
Последний шаг — это код функции FaceDetect.detect. Версия Java, см. Ранее упомянутую статью , очень легко конвертируется в версию Scala.
01
02
03
04
05
06
07
08
09
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
|
package javacv import com.googlecode.javacv.cpp.opencv _ core. _ import com.googlecode.javacv.cpp.opencv _ imgproc. _ import com.googlecode.javacv.cpp.opencv _ highgui. _ import com.googlecode.javacv.cpp.opencv _ objdetect. _ import com.googlecode.javacpp.BytePointer import java.nio.ByteBuffer import javax.imageio.ImageIO import java.io.ByteArrayOutputStream import scala.tools.nsc.io.VirtualFile object FaceDetect { var CASCADE _ FILE = PATH _ TO _ CASCADE _ FILE; var minsize = 20 ; var group = 0 ; var scale = 1.1 ; def detect(imageData : Array[Byte]) : Array[Byte] = { // we need to wrap the input array, since BytePointer doesn't accept // a bytearray as input. It accepts a byte varargs, but Array[Byte] // doesn't convert automatically var wrappedData = ByteBuffer.wrap(imageData); var originalImage = cvDecodeImage(cvMat( 1 , imageData.length,CV _ 8 UC 1 , new BytePointer(wrappedData))); // convert to grayscale for easy detection var grayImage = IplImage.create(originalImage.width(), originalImage.height(), IPL _ DEPTH _ 8 U, 1 ); cvCvtColor(originalImage, grayImage, CV _ BGR 2 GRAY); // storage is needed to store information during detection var storage = CvMemStorage.create(); // load and run the cascade var cascade = new CvHaarClassifierCascade(cvLoad(CASCADE _ FILE)); var faces = cvHaarDetectObjects(grayImage, cascade, storage, scale, group, minsize); // draw a rectangle for the detected faces for (i <- 0 until faces.total) { var r = new CvRect(cvGetSeqElem(faces, i)); cvRectangle(originalImage, cvPoint(r.x, r.y), cvPoint(r.x + r.width(), r.y + r.height), CvScalar.YELLOW, 1 , CV _ AA, 0 ); } // convert to bytearray and return var bout = new ByteArrayOutputStream(); var imgb = originalImage.getBufferedImage(); ImageIO.write(imgb, "png" , bout); bout.toByteArray() } } |
Единственная проблема, с которой я столкнулся, была с конструктором BytePointer. Одна из подписей принимает переменные типа byte. В Java это позволяет мне просто предоставить этому конструктору байт [], но в Scala это не работает. К счастью, BytePointer также может быть создан с использованием ByteBuffer. В остальном это преобразование Java в Scala.
Выполнение кода
И это почти все. По умолчанию play прослушивает порт 9000, в примере на базе Jetty сервер работал на 9999 и слушал корневой контекст. Для работы с нашим сервером на базе Play2 / Scala нам просто нужно указать браузеру правильный URL-адрес сервера веб-сокетов.
1
|
ws = new WebSocket( "ws://127.0.0.1:9000/wsrequest" ); |
И теперь, когда мы запускаем его, мы используем Play 2 в качестве нашего сервера и запускаем код JavaCV с помощью scal. И что еще более важно это все еще работает:
Ссылка: бинарные веб-сокеты с Play 2.0 и Scala (и чуть более JavaCV / OpenCV) от нашего партнера JCG Йоса Дирксена в блоге Smart Java .