最近要做一個Flex的演示程序,Flex本身不支持直接訪問數據庫,只能是由服務器端來間接訪問數據庫,在網上找到了一個ASSQL的SWC,可以直接通過Socket訪問Mysql數據庫,也就是不需要Flex服務器端就可以直接訪問MySQL服務器,這樣的好處是可以快速開發一個演示程序的原型,缺點是不適用於安全性要求高的Flex真正的運行應用,因爲它把數據庫連接的密碼用戶名打包進了客戶端的SWF文件中,安全性極差。
1.首先從http://code.google.com/p/assql/下載最新的Beta2.7的源代碼,注意最新的程序跟Flex SDK不兼容,需要手工修改代碼。比如我連接數據庫時遇到了一個1063的錯誤問題,需要修改
MySqlService.as文件中的private function handleError(info:Object):void {函數聲明爲 private function handleError(info:Object, token:Object=null):void {
爲了編譯assql,注意不要使用下載的工程文件(它同最新的Flex SDK不兼容好像),我們需要創建一個新的Flex Library Project,然後將所有的as文件複製到新建的工程文件中,並在項目的Flex Build Path中添加所有的AS文件,設定好後,Flex會自動編譯生成assql.swc文件。
2.接下來,創建一個使用assql的Flex應用,代碼如下
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:assql="com.maclema.mysql.mxml.*"
layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import com.maclema.mysql.events.MySqlErrorEvent;
import com.maclema.util.ResultsUtil;
private function handleConnected(e:Event):void {
Alert.show("connected ");
service.send("SELECT * FROM books ");
}
private function handleError(e:MySqlErrorEvent):void {
Alert.show(e.text);
}
]]>
</mx:Script>
<assql:MySqlService id="service"
hostname="localhost"
username="root"
password="root"
database="consultant_db"
autoConnect="true"
charSet="utf8"
connect="handleConnected(event)"
sqlError="handleError(event)" />
<mx:DataGrid id="grid" left="10" right="10" top="10" bottom="10"
dataProvider="{service.lastResult}"
columns="{ResultsUtil.getDataGridColumns(service.lastResultSet)}" />
</mx:Application>
注意,要將上面編譯好的assql.swc複製到Flex應用的Libs目錄下,這樣才能編譯通過。
運行後,我們就應該可以看到DataGrid中出現想要的結果了。
3.關於assql的中文顯示問題
用assql顯示中文記錄時會顯示爲亂碼,在網上查了一下,assql最新的代碼仍然沒有很好的解決這個問題,網上相關的討論對於最新的程序也是無效的,因爲assql是基於mysql的jdbc代碼改寫的,我下載了ConnectorJ的源代碼看了一下,改寫了一個臨時的解決方案,只對UTF8編碼的數據庫有效。
就是對所有的
writeMultiByte和readMultiByte的調用,都強制使用UTF-8的編碼方式,同時修改服務器握手的字符集設定的部分。
我修改後的版本見附件。
4.關於assql的安全認證
因爲assql使用了socket通訊,從客戶機器上直接進行socket通訊有安全性的問題,爲了保證安全性,Adobe Flash Player要求我們必須設置一個policy file 服務器提供一個安全定義文件,來允許我們針對某個端口進行socket通訊。下面就是監聽843端口的Serverlet的實現,它將服務器上D:/hubdog/Flex/tomcat/webapps/samples/WEB-INF/flashpolicy.xml這個文件傳給發起驗證請求的Flash Player
package com.maclema.flash;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.servlet.http.HttpServlet;
import org.apache.log4j.Logger;
public class PolicyServerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static ServerSocket serverSock;
private static boolean listening = true;
private static Thread serverThread;
private static Logger logger = Logger.getLogger(PolicyServerServlet.class);
static {
try {
serverThread = new Thread(new Runnable() {
public void run() {
try {
logger.debug("PolicyServerServlet: Starting...");
serverSock = new ServerSocket(843, 50);
while (listening) {
logger.debug("PolicyServerServlet: Listening...");
final Socket sock = serverSock.accept();
Thread t = new Thread(new Runnable() {
public void run() {
try {
logger.debug("PolicyServerServlet: Handling Request...");
sock.setSoTimeout(10000);
InputStream in = sock.getInputStream();
byte[] buffer = new byte[23];
if (in.read(buffer) != -1
&& (new String(buffer))
.startsWith("<policy-file-request/>")) {
logger.debug("PolicyServerServlet: Serving Policy File...");
// get the local tomcat path, and
// the path to our flashpolicy.xml
// file
File policyFile = new File(
"D:/hubdog/Flex/tomcat/webapps/samples/WEB-INF/flashpolicy.xml");
logger.debug("policy file:"+policyFile.getAbsolutePath());
BufferedReader fin = new BufferedReader(
new FileReader(policyFile));
OutputStream out = sock
.getOutputStream();
String line;
while ((line = fin.readLine()) != null) {
out.write(line.getBytes());
}
fin.close();
out.write(0x00);
out.flush();
out.close();
} else {
logger.debug("PolicyServerServlet: Ignoring Invalid Request");
logger.debug(" "
+ (new String(buffer)));
}
} catch (Exception ex) {
logger.debug("PolicyServerServlet: Error: "
+ ex.toString());
} finally {
try {
sock.close();
} catch (Exception ex2) {
}
}
}
});
t.start();
}
} catch (Exception ex) {
logger.debug("PolicyServerServlet: Error: "
+ ex.toString());
}
}
});
serverThread.start();
} catch (Exception ex) {
logger.debug("PolicyServerServlet Error---");
ex.printStackTrace(System.out);
}
}
public void destroy() {
logger.debug("PolicyServerServlet: Shutting Down...");
if (listening) {
listening = false;
}
if (!serverSock.isClosed()) {
try {
serverSock.close();
} catch (Exception ex) {
}
}
}
}
policy文件的內容如下
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" to-ports="3306" />
</cross-domain-policy>
爲了讓Web Server啓動時自動加載Serverlet,我們需要編輯Web.xml添加下面的定義
<servlet>
<servlet-name>PolicyServerServlet</servlet-name>
<servlet-class>com.maclema.flash.PolicyServerServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>PolicyServerServlet</servlet-name>
<url-pattern>/policyserver</url-pattern>
</servlet-mapping>
爲了驗證跨域的Flash請求,我們要將Flex客戶端的mysqlservice 組件的定義修改一下
<assql:MySqlService id="service"
hostname="192.168.0.2"
username="asroot"
password="root"
database="assql_db"
autoConnect="true"
charSet="utf8"
connect="handleConnected(event)"
sqlError="handleError(event)" />
注意這回Hostname不是Localhost,而是一個IP地址,同時注意用戶名asroot定義時,它允許的host name需要是%,否則從其他的機器登錄時,會報用戶訪問拒絕
這回將swf複製到另外一臺客戶端上,它的ip地址可以是192.168.0.3,然後運行swf,你會發現它確實可以從192.168.0.2的數據庫中取出數據來了。