此功能實現研究了很長時間,開始用C#寫的activex外掛程式,實現起來比較容易,服務端也可以直接寫入到檔案夾中,可是有個缺點就是用戶端必須安裝framework,這個對客戶來說是不現實的。
後來研究用c++實現activex,post上傳到伺服器url,然後接受post檔案,由於對c++不熟悉,搞了很長時間沒搞出來,可能還是我對c++不會使用的原因,不過搜遍Google和百度,也沒搜到正解。
最後用dephi實現的activex,很簡單就實現了,同事給寫的代碼,不到20行,由此對dephi產生敬意,用戶端也不必安裝環境,然後對ocx外掛程式進行簽名後測試正常使用。
一.開發背景
本功能基於ckeditor的版本是3.6.2。當使用者從瀏覽器用戶端的word或wps裡複製粘貼圖片(jpg,gif,png,bmp,jepg格式)至ckeditor控制項時,文字框框中顯示的圖片只是在windows的c盤臨時檔案夾下,在點擊儲存文本時,文本中的圖片並沒有傳至伺服器,如果想上傳圖片,只能一張一張的上傳圖片。這是因為IE及ckeditor為了網站安全考慮,不能將未加審核的檔案直接上傳到伺服器上。
為了方便使用者可以將word或wps編輯好的內容和圖片,直接拷貝上傳到伺服器,不必單張傳送,所以開發此外掛程式。
二.外掛程式實現原理
1.頁面內建上傳圖片外掛程式UploadImgActivex.ocx,用戶端安裝後可以讀取文字編輯器中的本地圖片,然後以post方式傳到伺服器的接受頁面。
2.伺服器端接受頁面,上傳圖片流的頁面,伺服器接受外掛程式post的檔案流然後儲存至檔案夾下即可。
3.UploadImgActivex.ocx的實現只有一個方法:參數是兩個(上傳目標url,上傳的用戶端檔案路徑)
如下所示UploadImgActivex.UpLoadToServer("http://192.168.0.133:3011/imgUpload.ashx?newfilename=" + newImageName, clientImgPath);
三.具體配置過程
1. html或動態網頁面(asp,aspx,jsp,php)裡添加object和ckeditor:
<object id=" objUploadImgActivex" classid="clsid:C1A70DED-FB71-4494-B18C-AD9755FAD693"
codebase="activex/UploadImgActivex.ocx"
width="100"
height="100" style=" display:none;"></object>
<textarea id="editor1" name="editor1"></textarea>
<script type="text/javascript">
//<![CDATA[
CKEDITOR.replace('editor1',
{
fullPage: true,
extraPlugins:
'uploadImg,docprops'
});
//]]>
</script>
注意:在extraPlugins裡面,記得添加uploadImg,表示添加外掛程式
2. 在ckeditor裡添加按鈕功能,用外掛程式來實現:
配置ckeditor/config.js要包含外掛程式uploadImg:
config.toolbar_Full =
[
{ name: 'document', items: ['uploadImg', 'Source', '-', 'Save', 'NewPage', 'DocProps', 'Preview', 'Print', '-', 'Templates'] },
{ name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
{ name: 'editing', items: ['Find', 'Replace', '-', 'SelectAll', '-', 'SpellChecker', 'Scayt'] },
{ name: 'forms', items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton','HiddenField']
},
'/',
{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat'] },
{ name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv','-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'] },
{ name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
{ name: 'insert', items: ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe'] },
'/',
{ name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize'] },
{ name: 'colors', items: ['TextColor', 'BGColor'] },
{ name: 'tools', items: ['Maximize', 'ShowBlocks', '-', 'About'] }
];
3. 在檔案\ckeditor\plugins\uploadImg\plugin.js中配置上傳伺服器後的檔案儲存路徑和伺服器端接受外掛程式上傳的檔案流的頁面:
exec: function (editor) {
//alert("這是自訂按鈕,圖片上傳");
//存放上傳圖片的絕對路徑 伺服器存放目錄serverImgPath 用戶端圖片路徑clientImgPath
var serverImgPath = "http://192.168.0.133:3011/uploadImg/";
var oEditor = CKEDITOR.instances.editor1;
var text = oEditor.getData();
//Regex挑選圖片
var regularText = /file:\/\/\/(\S+)(\.jpg|\.jpeg|\.png|\.bmp|\.gif){1}/gi;
var arrData = text.match(regularText);
if (null != arrData) {
for (var i = 0; i < arrData.length; i++) {
var clientImgPath = arrData[i].substr(8);
var newImageName = getFileName() + i.toString() + clientImgPath.substr(clientImgPath.lastIndexOf("."));
//尋找外掛程式,利用外掛程式進行圖片上傳
var a = document.getElementById("objUploadImgActivex");
//接受外掛程式post的資料,newfilename是新檔案存放名稱,clientImgPath是本地上傳的圖片路徑(c:/test.jpg)
var result = a.UpLoadToServer("http://192.168.0.133:3011/imgUpload.ashx?newfilename=" + newImageName, clientImgPath);
text = text.replace(arrData[i], serverImgPath + newImageName);
}
alert("圖片上傳完畢,共上傳" + arrData.length + "張圖片!");
oEditor.setData(text);
}
else {
alert("沒有要上傳的圖片!");
}
}
4. 伺服器端接受檔案流並儲存(以下是asp.net中利用ashx接受檔案流代碼):
public class imgUpload : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//context.Response.ContentType = "text/plain";
//context.Response.Write("Hello World");
//App.Log.Info(context.Request.Url.ToString());
//擷取上傳的資料流
string fileNameStr = DateTime.Now.ToString("yyyy-MM-ddHHmmssfff"); //context.Request.QueryString["fileName"];
Stream sr = context.Request.InputStream;
string newfilename = context.Request.QueryString["newfilename"];
try
{
string filename = fileNameStr;
byte[] buffer = new byte[4096];
int bytesRead = 0;
//將當前資料流寫入伺服器端檔案夾ClientBin下
string targetPath = context.Server.MapPath("~/uploadImg/" + newfilename);
using (FileStream fs = File.Create(targetPath, 4096))
{
while ((bytesRead = sr.Read(buffer, 0, buffer.Length)) > 0)
{
//向檔案中寫資訊
fs.Write(buffer, 0, bytesRead);
}
}
context.Response.ContentType = "text/plain";
context.Response.Write("上傳成功"+newfilename);
}
catch (Exception e)
{
context.Response.ContentType = "text/plain";
context.Response.Write("上傳失敗, 錯誤資訊:" + e.Message);
//App.Log.Info(e.Message);
}
finally
{
sr.Dispose();
}
}
5. 配置完成後即可,粘貼word或wps內容進行測試:
點擊上傳,即可將圖片傳到伺服器 :
注意事項:
1.外掛程式只能在IE瀏覽器下使用,包括IE6,IE7,IE8,IE9,或者IE核心的瀏覽器。
2.用戶端訪問html或動態網頁面時,因為要安裝外掛程式,會有提示資訊:
點擊“為此電腦的所有使用者安裝此附加元件”。
3.如果提示安裝失敗,請先點擊瀏覽器“工具”—“Internet選項”---“安全”—“自訂設定”:
在下在未簽名的ActiveX控制項(不安全)中選擇:啟用(不安全)
[code=delphi]// UpImage.cpp : Implementation of CActiveXUpImgApp and DLL registration.#include "stdafx.h"#include "ActiveXUpImg.h"#include "UpImage.h"#include "string"#include "iostream"#include "stdio.h"#include "Wininet.h"#pragma comment(lib, "Wininet.lib")#define BUF_SIZE 1024using namespace std;///////////////////////////////////////////////////////////////////////////////STDMETHODIMP UpImage::InterfaceSupportsErrorInfo(REFIID riid){static const IID* arr[] = {&IID_IUpImage,};for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++){if (InlineIsEqualGUID(*arr[i],riid))return S_OK;}return S_FALSE;}STDMETHODIMP UpImage::UploadImg2Server(long a,long b){// TODO: Add your implementation code herechar* Ip="192.168.0.133";int port=3011;char* upFile="c:/kinectsensor.jpg";HINTERNET hSession=NULL, hConnect=NULL, hRequest=NULL; hSession = InternetOpen("test", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); hConnect = InternetConnect(hSession, Ip, port,//INTERNET_DEFAULT_HTTP_PORT, NULL, NULL,INTERNET_SERVICE_HTTP, INTERNET_FLAG_NO_CACHE_WRITE, NULL); INTERNET_BUFFERS BufferIn = {0}; DWORD dwBytesRead; DWORD dwBytesWritten; BYTE pBuffer[1024]; // Read from file in 1K chunks BOOL bRead, bRet; BufferIn.dwStructSize = sizeof(INTERNET_BUFFERS); hRequest = HttpOpenRequest(hConnect, "POST", "xml_convert", NULL, NULL, NULL, 0, 0); // xml_convert請求實體 if (!hRequest) { printf("Failed to open request handle: %lu\n", GetLastError ()); return FALSE; } HANDLE hFile = CreateFile(upFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("\nFailed to open local file %s.", upFile); return FALSE; } BufferIn.dwBufferTotal = GetFileSize(hFile, NULL); printf ("File size is %d\n", BufferIn.dwBufferTotal ); if(!HttpSendRequestEx(hRequest, &BufferIn, NULL, HSR_INITIATE, 0)) { printf("Error on HttpSendRequestEx %lu\n",GetLastError()); return FALSE; } DWORD sum = 0; do { if (!(bRead = ReadFile(hFile, pBuffer, sizeof(pBuffer), &dwBytesRead, NULL))) { printf ("\nReadFile failed on buffer %lu.",GetLastError()); break; } if (!(bRet=InternetWriteFile( hRequest, pBuffer, dwBytesRead, &dwBytesWritten))) { printf ("\nInternetWriteFile failed %lu", GetLastError()); break; } sum += dwBytesWritten; } while (dwBytesRead == sizeof(pBuffer)) ; CloseHandle(hFile); printf ("Actual written bytes: %d\n", sum); if(!HttpEndRequest(hRequest, NULL, 0, 0)) { printf( "Error on HttpEndRequest %lu \n", GetLastError()); return FALSE; } int contextLengthId = HTTP_QUERY_CONTENT_LENGTH; int statusCodeId = HTTP_QUERY_STATUS_CODE; int statusTextId = HTTP_QUERY_STATUS_TEXT; char szBuf[BUF_SIZE] = {0}; DWORD dwSize = BUF_SIZE; char pcBuffer[BUF_SIZE]; DWORD dwByteRead; //printf("\n\n服務端返回如下:\n"); do { dwBytesRead = 0; if(InternetReadFile(hRequest, pcBuffer, BUF_SIZE - 1, &dwByteRead)) {pcBuffer[dwByteRead] = 0x00; // Null-terminate bufferprintf("%s", pcBuffer);memset(pcBuffer, 0, BUF_SIZE); }else{ printf("InternetReadFile failed\n"); } }while(dwByteRead>0); /*if (HttpQueryInfo(hRequest, statusTextId, szBuf, &dwSize, 0)) {szBuf[dwSize] = 0; printf("Status text:[%s]\n", szBuf); }*/ //cout<<"上傳成功"<<endl; return TRUE;return S_OK;}[/code]