此功能实现研究了很长时间,开始用C#写的activex插件,实现起来比较容易,服务端也可以直接写入到文件夹中,可是有个缺点就是客户端必须安装framework,这个对客户来说是不现实的。
后来研究用c++实现activex,post上传到服务器url,然后接受post文件,由于对c++不熟悉,搞了很长时间没搞出来,可能还是我对c++不会使用的原因,不过搜遍谷歌和百度,也没搜到正解。
最后用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();
//正则表达式挑选图片
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 1024
using 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 here
char* 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 buffer
printf("%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]