剛好案子的需要,透過Windows Form 把圖片檔上傳,但我對於底層的技術Socket 不是很擅長
於是我透過WCF將圖片上傳到server 端,同樣達到同樣的功能,以下記錄自我學習過程。
案例一
|
WCF傳輸圖片
|
啟動者
|
Foxpro程式
|
主要流程:
|
|
1.
輸入客戶醫事機構號、診所名稱、檔名、路徑
2.
經由WCF傳輸圖片,並接收圖片儲存成『醫事機構號_診所名稱/年月日/檔名.jpg』
3.
回傳status : -1初始值 0:失敗 1:成功
4.
回傳ErrorMsg: 空白(成功執行,並未回傳錯誤訊息)
|
|
案件圖
|
|
|
|
範例
|
|
CooperXSend cooperXSendObj = new CooperXSend();
cooperXSendObj.ClinicNo = "1001";
cooperXSendObj.ClinicName = "診所";
cooperXSendObj.FilePath = "C:\Users\Public\Pictures\Sample
Pictures";
cooperXSendObj.FileName
= "Jellyfish.jpg";
cooperXSendObj.SendImage();
Console.Write(cooperXSendObj.Status +
cooperXSendObj.ErrorMsg);
|
步驟一: 在server端架設WCF
IService.cs
[ServiceContract]
public interface IService
{
[OperationContract]
isPassOK SaveStreamToFile(RemoteFileInfo request);
[OperationContract]
isPassOK SaveLog(SaveDBLog saveSqlLog);
}
[MessageContract]
public class SaveDBLog
{
[MessageHeader(MustUnderstand = true)]
public string
SqlServer;
[MessageHeader(MustUnderstand = true)]
public string
SqlDbName;
[MessageHeader(MustUnderstand = true)]
public string
SqlUserID;
[MessageHeader(MustUnderstand = true)]
public string
SqlPassword;
[MessageHeader(MustUnderstand = true)]
public string
ClinicNo; //醫事機構號
[MessageHeader(MustUnderstand = true)]
public string
ClinicName;//診所名稱
[MessageHeader(MustUnderstand = true)]
public string
Physavepath;//儲存檔案實體路徑
[MessageHeader(MustUnderstand = true)]
public string
YearMonth; //申報費用年月
[MessageHeader(MustUnderstand = true)]
public string
CaseNO; //案件類別
[MessageHeader(MustUnderstand = true)]
public string FlowKey; //案件流水號
[MessageHeader(MustUnderstand = true)]
public string
FileName;//檔名含路徑
[MessageHeader(MustUnderstand = true)]
public string
SendImageErrorMsg;
}
|
宣告WCF的介面時,如果有使用到物件類別時,需用MessageContract 宣告成物件,MessageHeader 宣告物件屬性,用OperationContract 宣告在WCF中所有使用的方法
Service.cs
[ServiceBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
public isPassOK SaveStreamToFile(RemoteFileInfo request)
{
isPassOK isPassOKObj = new isPassOK();
try
{
isPassOKObj.strErrorMsg = "";
//設定儲存圖片的根路徑
string strRootFile = ConfigurationManager.AppSettings["rootPath"].ToString();
string strFirstDirectory = request.ClinicNo;
string strSecondDirectory = request.YearMonth; //改成費用年月路徑
string strSavePath = strRootFile + "\\" + strFirstDirectory + "\\" + strSecondDirectory;
string strSaveName = request.FileName;
Stream stream = request.FileByteStream;
string fileFullPath = request.FileName;
if (stream.Length == 0)
{
isPassOKObj.strErrorMsg = "無串流資料";
return isPassOKObj;
}
#region 檢查是否需建立資料夾
if (!Directory.Exists(strRootFile))
{
Directory.CreateDirectory(strRootFile);
}
if (!Directory.Exists(strRootFile + "\\" + strFirstDirectory))
{
Directory.CreateDirectory(strRootFile + "\\" + strFirstDirectory);
}
if (!Directory.Exists(strRootFile + "\\" + strFirstDirectory + "\\" + strSecondDirectory))
{
Directory.CreateDirectory(strRootFile + "\\" + strFirstDirectory + "\\" + strSecondDirectory);
}
#endregion
#region 寫檔案
using (FileStream fileStream = System.IO.File.Create(strSavePath
+ "\\" +
strSaveName, (int)stream.Length))
{
int bufferLen = 65000;
byte[] bytesInStream = new byte[stream.Length];
int offset = 0;
int totalBytes = (int)stream.Length;
while (totalBytes > 0)
{
//萬一圖片太小
if (bufferLen > totalBytes)
{
bufferLen
= totalBytes;
}
int n = stream.Read(bytesInStream, offset, bufferLen);
fileStream.Write(bytesInStream, offset, bufferLen);
if (n != 0)
{
offset +=
n - 1;
totalBytes
-= n;
if (totalBytes < bufferLen)
{
bufferLen = totalBytes;
}
}
}
fileStream.Close();
stream.Close();
isPassOKObj =
isCheckedFileExist(strSavePath + "\\" + strSaveName,request.CRC);
SaveDBLog saveDbLog = new SaveDBLog();
saveDbLog.ClinicName = request.ClinicName;
saveDbLog.Physavepath = strSavePath + "\\";
saveDbLog.ClinicNo
= request.ClinicNo;
saveDbLog.YearMonth = request.YearMonth;
saveDbLog.CaseNO =
request.CaseNO;
saveDbLog.FlowKey
= request.FlowKey;
saveDbLog.FileName
= request.FileName;
saveDbLog.SqlServer = ConfigurationManager.AppSettings["DBServer"].ToString();
saveDbLog.SqlDbName
= ConfigurationManager.AppSettings["DBName"].ToString();
saveDbLog.SqlUserID = ConfigurationManager.AppSettings["DBUserID"].ToString();
saveDbLog.SqlPassword = ConfigurationManager.AppSettings["DBPassword"].ToString();
saveDbLog.SendImageErrorMsg = isPassOKObj.strErrorMsg;
isPassOKObj.strErrorMsg += ((isPassOK)SaveLog(saveDbLog)).strErrorMsg; //寫入資料庫
}
#endregion
}
catch (Exception ex)
{
isPassOKObj.strErrorMsg = ex.Message;
}
return isPassOKObj;
}
public isPassOK isCheckedFileExist(string
strFileName, string strCheckSum)
{
isPassOK isPassOkObj = new isPassOK();
long intCheckSumTo16=0; //foxpro 傳進的檢查碼
long intHashSumTo16 = 0; //c# 算出的檢查碼
try
{
FileInfo fi = new FileInfo(strFileName);
if (fi.Exists)
{
CRC32 crc32 = new CRC32();
String hash = String.Empty;
using (FileStream fs = File.Open(strFileName,
FileMode.Open))
{
byte[] b = crc32.ComputeHash(fs);
for (int i =
b.Length-1; i >= 0; i--)
{
hash +=
b[i].ToString("x2").ToLower(); //每次加總bites 的checksum
}
}
//由客戶端加總的檢查碼
intCheckSumTo16 = Convert.ToInt64(strCheckSum);
//如果數字太大會溢位,所以使用long,
//伺服器轉換成16進位的checksum
intHashSumTo16 = Int64.Parse(hash, System.Globalization.NumberStyles.HexNumber);
//比較伺服器和客戶端的檢查碼
if (intHashSumTo16 != intCheckSumTo16)
{
isPassOkObj.strErrorMsg = "比對檢核碼不一致";
}
}
else
{
isPassOkObj.strErrorMsg = "查無此檔案";
}
return isPassOkObj;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
|
作者在架設WCF發生錯誤訊息,請參考http://www.roelvanlisdonk.nl/?p=1232
,AspNetCompatibilityRequirements 屬性為TURE 加上此設定後,情況就正常了。
根據MSDN官方說法
當AspNetCompatibilityRequirements =false 時,WCF Service 預設執行Mixed Transports Mode,所以要明確的說明兼容模式。
回到正題,圖片從客戶端傳送到伺服器端,中間也許有封包遺失,圖片會上傳不完整…
所以要有檢查機制,告訴客戶端目前是否以傳送完成。
所以透過CRC32 的CheckSum 檢查
// Tamir Khason http://khason.net/
//
// Released under MS-PL : 6-Apr-09
using System;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Text;
/// <summary>Implements
a 32-bits cyclic redundancy check (CRC) hash algorithm.</summary>
/// <remarks>This
class is not intended to be used for security purposes. For security
applications use MD5, SHA1, SHA256, SHA384,
/// or SHA512 in the System.Security.Cryptography namespace.</remarks>
public class CRC32 : HashAlgorithm {
#region CONSTRUCTORS
/// <summary>Creates
a CRC32 object using the <see
cref="DefaultPolynomial"/>.</summary>
public CRC32() : this(DefaultPolynomial)
{
}
/// <summary>Creates
a CRC32 object using the specified polynomial.</summary>
/// <remarks>The
polynomical should be supplied in its bit-reflected form. <see cref="DefaultPolynomial"/>.</remarks>
[CLSCompliant(false)]
public CRC32(uint polynomial) {
HashSizeValue = 32;
_crc32Table = (uint[])_crc32TablesCache[polynomial];
if (_crc32Table == null) {
_crc32Table = CRC32._buildCRC32Table(polynomial);
_crc32TablesCache.Add(polynomial, _crc32Table);
}
Initialize();
}
//
static constructor
static CRC32() {
_crc32TablesCache = Hashtable.Synchronized(new Hashtable());
_defaultCRC = new CRC32();
}
#endregion
#region PROPERTIES
/// <summary>Gets
the default polynomial (used in WinZip, Ethernet, etc.)</summary>
/// <remarks>The
default polynomial is a bit-reflected version of the standard polynomial
0x04C11DB7 used by WinZip, Ethernet, etc.</remarks>
[CLSCompliant(false)]
public static readonly uint DefaultPolynomial = 0xEDB88320; // Bitwise reflection of 0x04C11DB7;
#endregion
#region
METHODS
/// <summary>Initializes
an implementation of HashAlgorithm.</summary>
public override void Initialize() {
_crc = _allOnes;
}
/// <summary>Routes
data written to the object into the hash algorithm for computing the hash.</summary>
protected override void HashCore(byte[] buffer, int offset, int
count) {
for (int i = offset; i < count; i++) {
ulong ptr = (_crc & 0xFF) ^ buffer[i];
_crc >>= 8;
_crc ^=
_crc32Table[ptr];
}
}
/// <summary>Finalizes
the hash computation after the last data is processed by the cryptographic
stream object.</summary>
protected override byte[] HashFinal() {
byte[] finalHash = new byte[4];
ulong finalCRC = _crc ^ _allOnes;
finalHash[0] = (byte)((finalCRC >> 0) & 0xFF);
finalHash[1] = (byte)((finalCRC >> 8) & 0xFF);
finalHash[2] = (byte)((finalCRC >> 16) & 0xFF);
finalHash[3] = (byte)((finalCRC >> 24) & 0xFF);
return finalHash;
}
/// <summary>Computes
the CRC32 value for the given ASCII string using the <see
cref="DefaultPolynomial"/>.</summary>
public static int Compute(string asciiString) {
_defaultCRC.Initialize();
return ToInt32(_defaultCRC.ComputeHash(asciiString));
}
/// <summary>Computes
the CRC32 value for the given input stream using the <see
cref="DefaultPolynomial"/>.</summary>
public static int Compute(Stream
inputStream) {
_defaultCRC.Initialize();
return ToInt32(_defaultCRC.ComputeHash(inputStream));
}
/// <summary>Computes
the CRC32 value for the input data using the <see
cref="DefaultPolynomial"/>.</summary>
public static int Compute(byte[] buffer) {
_defaultCRC.Initialize();
return ToInt32(_defaultCRC.ComputeHash(buffer));
}
/// <summary>Computes
the hash value for the input data using the <see
cref="DefaultPolynomial"/>.</summary>
public static int Compute(byte[] buffer, int offset, int
count) {
_defaultCRC.Initialize();
return ToInt32(_defaultCRC.ComputeHash(buffer, offset, count));
}
/// <summary>Computes
the hash value for the given ASCII string.</summary>
/// <remarks>The
computation preserves the internal state between the calls, so it can be used
for computation of a stream data.</remarks>
public byte[] ComputeHash(string asciiString) {
byte[] rawBytes = ASCIIEncoding.ASCII.GetBytes(asciiString);
return ComputeHash(rawBytes);
}
/// <summary>Computes
the hash value for the given input stream.</summary>
/// <remarks>The
computation preserves the internal state between the calls, so it can be used
for computation of a stream data.</remarks>
new public byte[] ComputeHash(Stream inputStream) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, 4096)) > 0) {
HashCore(buffer, 0,
bytesRead);
}
return HashFinal();
}
/// <summary>Computes
the hash value for the input data.</summary>
/// <remarks>The
computation preserves the internal state between the calls, so it can be used
for computation of a stream data.</remarks>
new public byte[] ComputeHash(byte[]
buffer) {
return ComputeHash(buffer, 0, buffer.Length);
}
/// <summary>Computes
the hash value for the input data.</summary>
/// <remarks>The
computation preserves the internal state between the calls, so it can be used
for computation of a stream data.</remarks>
new public byte[] ComputeHash(byte[]
buffer, int offset, int count) {
HashCore(buffer, offset,
count);
return HashFinal();
}
#endregion
#region PRIVATE SECTION
private static uint _allOnes = 0xffffffff;
private static CRC32 _defaultCRC;
private static Hashtable _crc32TablesCache;
private uint[] _crc32Table;
private uint _crc;
//
Builds a crc32 table given a polynomial
private static uint[] _buildCRC32Table(uint
polynomial) {
uint crc;
uint[] table = new uint[256];
// 256
values representing ASCII character codes.
for (int i = 0; i < 256; i++) {
crc = (uint)i;
for (int j =
0; j < 8; j++) {
if ((crc & 1) == 1)
crc = (crc
>> 1) ^ polynomial;
else
crc >>= 1;
}
table[i] = crc;
}
return table;
}
private static int ToInt32(byte[] buffer) {
return BitConverter.ToInt32(buffer,
0);
}
#endregion
}
|
步驟二: 在client 加入服務參考
用戶端à端點à新增端點
輸入你專案的契約
每個屬性都有其意義,生怕才疏學淺的我,怕錯誤的觀念帶給大家,所以屬性值僅供參考而已
自動產生出來的設定值,可直接對app.config
直接調整
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://192.168.1.222:85/service.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService" contract="ServiceReference1.IService"
name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
</configuration>
|
客戶端的程式
public void SendImage()
{
WriteEvent.WrittingEventLog writeObj = new WriteEvent.WrittingEventLog();
try
{
ServiceClient wcfSendImage = new ServiceClient();
//檢查檔名存在與否
string strFullPathFileName = Path.Combine(FilePath, FileName);
if (!File.Exists(strFullPathFileName))
{
_status = 0;
_errorMsg = "檔案不存在";
}
else
{
#region 寫入圖片
FileInfo fileInfo = new FileInfo(strFullPathFileName);
var stream = new MemoryStream();
using (var file
= File.OpenRead(strFullPathFileName))
{
file.CopyTo(stream);
}
stream.Position = 0L;
RemoteFileInfo remoteFileObj = new RemoteFileInfo();
remoteFileObj.ClinicName = ClinicName;
remoteFileObj.ClinicNo = ClinicNo;
remoteFileObj.YearMonth
= YearMonth;
remoteFileObj.CaseNO = CaseNo;
remoteFileObj.FlowKey = FlowKey;
remoteFileObj.FilePath = FilePath;
remoteFileObj.FileName = FileName; //純檔案名稱
remoteFileObj.CRC = CRC;
remoteFileObj.FileByteStream = stream;
//呼叫WCF儲存至SERVER端
_errorMsg =
wcfSendImage.SaveStreamToFile( remoteFileObj.CRC, remoteFileObj.CaseNO,
remoteFileObj.ClinicName, remoteFileObj.ClinicNo, remoteFileObj.FileName,
remoteFileObj.FilePath,remoteFileObj.FlowKey, remoteFileObj.YearMonth,
remoteFileObj.FileByteStream);
#endregion
}
//代表有錯誤訊息
if
(_errorMsg != "")
{
//not
pass
writeObj.writeToFile(_errorMsg);
_status = 0;
}
else
{
//pass
_status = 1;
}
}
catch (Exception ex)
{
writeObj.writeToFile(ex.Message);
_status = 0;
_errorMsg = "非預期錯誤:" + ex.Message;
}
}
|
感謝大家有耐心的讀完… 希望對大家有所收穫
沒有留言:
張貼留言