Skip to content

Instantly share code, notes, and snippets.

@seraphy
Created March 9, 2012 10:06
Show Gist options
  • Save seraphy/2005926 to your computer and use it in GitHub Desktop.
Save seraphy/2005926 to your computer and use it in GitHub Desktop.
ODP.NET/C# を使ってストアどプロシージャに一時BLOBデータを渡し方、およびユーザー定義型での受け渡し方のサンプル
create TYPE ATTACHED_FILE IS object(
FILE_NAME NVARCHAR2(256),
content blob
);
/
CREATE OR REPLACE package ODP_BLOB_TEST
is
type ATTACHED_FILES is table of ATTACHED_FILE index by BINARY_INTEGER;
/**
* ファイル名とBLOBを受け取りファイルに出力する.
* 出力先ディレクトリはTEMP_DIRディレクトリオブジェクトが示す場所となる.
* TEMP_DIRというディレクトリオブジェクトが作成済みであり、
* 読み書き権限が与えられていること.
*/
procedure WRITE_BLOB(P_FILE_NAME in nvarchar2, P_BINARY in blob);
/**
* ファイル名とBLOBをユーザー定義タイプで受け取りファイルに出力する.
*/
procedure WRITE_BLOB2(P_PARAM in ATTACHED_FILE);
/**
* ファイルへの出力テスト
*/
procedure SELF_TEST;
end;
/
CREATE OR REPLACE package body ODP_BLOB_TEST as
/**
* ファイル名とBLOBを受け取りファイルに出力する.
* 出力先ディレクトリはTEMP_DIRディレクトリオブジェクトが示す場所となる.
* TEMP_DIRというディレクトリオブジェクトが作成済みであり、
* 読み書き権限が与えられていること.
*/
procedure WRITE_BLOB(P_FILE_NAME in nvarchar2, P_BINARY in blob) as
VHANDLE UTL_FILE.FILE_TYPE;
BUF raw(1152); -- BASE64変換後のサイズ(読み込みバイトの1.5倍)
AMOUNT BINARY_INTEGER := 768; -- 48の倍数 (3と4で割り切れる数の1行単位);
POS PLS_INTEGER := 1;
BLOB_LEN PLS_INTEGER;
begin
VHANDLE := UTL_FILE.FOPEN('TEMP_DIR', P_FILE_NAME, 'wb');
if P_BINARY is not null and p_file_name is not null then
BLOB_LEN := DBMS_LOB.GETLENGTH(P_BINARY);
WHILE POS < BLOB_LEN
LOOP
DBMS_LOB.read(P_BINARY, AMOUNT, POS, BUF);
UTL_FILE.PUT_RAW(
VHANDLE,
BUF, -- バイナリそのまま転送
true
);
buf := NULL;
POS := POS + AMOUNT;
END LOOP;
UTL_FILE.FCLOSE(VHANDLE);
end if;
EXCEPTION when OTHERS then
UTL_FILE.FCLOSE(VHANDLE);
RAISE;
end WRITE_BLOB;
/**
* ファイル名とBLOBをユーザー定義タイプで受け取りファイルに出力する.
*/
procedure WRITE_BLOB2(P_PARAM in ATTACHED_FILE) as
begin
WRITE_BLOB(P_PARAM.FILE_NAME, P_PARAM.content);
end WRITE_BLOB2;
/**
* ファイルへの出力テスト
*/
procedure SELF_TEST as
lb blob;
ST timestamp;
EN timestamp;
-- 32Kを超えるバイナリデータをBLOB上に作成する
procedure generate_message(lb in out nocopy blob, cnt number) is
mes varchar2(4096);
rd raw(4096);
begin
for idx in 1..cnt
loop
mes := 'メッセージNo. ' || to_char(idx) || chr(10);
rd := UTL_I18N.STRING_TO_RAW(mes, 'UTF8');
DBMS_LOB.WRITEAPPEND(lb, UTL_RAW.LENGTH(rd), rd);
end LOOP;
end;
begin
-- 一時BLOBを構築、寿命を呼び出しスコープに設定
DBMS_LOB.CREATETEMPORARY(lb, TRUE, DBMS_LOB.CALL);
-- 32Kを超えるデータを作成
generate_message(lb, 23456);
-- 長さの検証
st := systimestamp;
DBMS_OUTPUT.PUT_LINE('len=' || DBMS_LOB.GETLENGTH(LB) || '/' || ST);
-- ファイルに書き出し
WRITE_BLOB('TEST.TXT', lb);
en := systimestamp;
dbms_output.put_line(en || '/span=' || (en - st));
-- 一時LOBの破棄
DBMS_LOB.FREETEMPORARY(lb);
end;
end ODP_BLOB_TEST;
/
using System;
using System.Configuration;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
namespace odptest
{
class Program
{
static void Main(string[] args)
{
// コネクション
string connStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (var conn = new OracleConnection(connStr))
{
conn.Open();
// ODP_BLOB_TEST.WRITE_BLOBメソッドを呼び出しを繰り返す
foreach (var idx in Enumerable.Range(0, 10))
{
var fileName = string.Format("TEST-A-{0}.txt", idx);
WriteBlob(conn, fileName);
}
// ODP_BLOB_TEST.WRITE_BLOB2メソッドを呼び出しを繰り返す
foreach (var idx in Enumerable.Range(0, 10))
{
var fileName = string.Format("TEST-B-{0}.txt", idx);
WriteBlob2(conn, fileName);
}
conn.Clone();
}
}
/// <summary>
/// コネクションに関連づけられた一時Blobを作成しテストデータを設定して返す.
/// コネクションはオープンされていなければならない.
/// (コネクションが閉じられることでBlobは無効となる.)
/// </summary>
/// <param name="conn">コネクション</param>
/// <returns>テストデータ設定済みの一時Blob</returns>
static OracleBlob MakeBlob(OracleConnection conn)
{
// コネクションがオープンされている状態でコンストラクタにより
// 一時Blobとして構築することができる.
var blob = new OracleBlob(conn);
// BLOBデータを作成する.
var enc = System.Text.Encoding.UTF8;
foreach (var idx in Enumerable.Range(0, 2000))
{
string mes = string.Format(
"Hello, World! {0}:{1:yyyy/MM/dd hh:mm:ss}\r\n",
idx,
DateTime.Now
);
byte[] buf = enc.GetBytes(mes);
// リモートにデータが送り込まれる
blob.Append(buf, 0, buf.Length);
}
// リワインド
blob.Position = 0;
return blob;
}
/// <summary>
/// ODP_BLOB_TEST.SEND_BLOBメソッドを呼び出す.
/// </summary>
/// <param name="conn">コネクション</param>
/// <param name="fileName">ファイル名</param>
static void WriteBlob(OracleConnection conn, string fileName)
{
using (OracleBlob blob = MakeBlob(conn))
{
using (var cmd = new OracleCommand())
{
cmd.Connection = conn;
cmd.CommandText = "begin ODP_BLOB_TEST.WRITE_BLOB(:FILE_NAME, :DATA); end;";
cmd.CommandType = System.Data.CommandType.Text;
cmd.BindByName = true;
var param1 = new OracleParameter("FILE_NAME", OracleDbType.NVarchar2);
param1.Direction = System.Data.ParameterDirection.Input;
param1.Value = fileName;
cmd.Parameters.Add(param1);
var param2 = new OracleParameter("DATA", OracleDbType.Blob);
param2.Direction = System.Data.ParameterDirection.Input;
param2.Value = blob;
cmd.Parameters.Add(param2);
cmd.ExecuteNonQuery();
}
blob.Close();
}
}
/// <summary>
/// ODP_BLOB_TEST.SEND_BLOBメソッドを呼び出す.
/// </summary>
/// <param name="conn">コネクション</param>
/// <param name="fileName">ファイル名</param>
static void WriteBlob2(OracleConnection conn, string fileName)
{
using (OracleBlob blob = MakeBlob(conn))
{
using (var cmd = new OracleCommand())
{
cmd.Connection = conn;
cmd.CommandText = "begin ODP_BLOB_TEST.WRITE_BLOB2(:ATTACHED_FILE); end;";
cmd.CommandType = System.Data.CommandType.Text;
cmd.BindByName = true;
var attachedFile = new AttachedFile();
attachedFile.FileName = fileName;
attachedFile.Content = blob;
var param1 = new OracleParameter("ATTACHED_FILE", OracleDbType.Object);
param1.Direction = System.Data.ParameterDirection.Input;
param1.UdtTypeName = "ODPNET.ATTACHED_FILE";
param1.Value = attachedFile;
cmd.Parameters.Add(param1);
cmd.ExecuteNonQuery();
}
blob.Close();
}
}
}
/// <summary>
/// CREATE TYPE IS ATTACHED_FILE IS OBJECT(FILE_NAME VARCHAR2, CONTENT BLOB);
/// のユーザー定義型に対するODP.NETのカスタム型の定義
/// </summary>
public class AttachedFile : IOracleCustomType, INullable
{
/// <summary>
/// フィールド名: FILE_NAME
/// </summary>
public const string KEY_FILE_NAME = "FILE_NAME";
// フィールド名: CONTENT
public const string KEY_CONTENT = "CONTENT";
/// <summary>
/// FILE_NAME属性
/// </summary>
[OracleObjectMapping(KEY_FILE_NAME)]
public string FileName { set; get; }
/// <summary>
/// CONTENT属性
/// </summary>
[OracleObjectMapping(KEY_CONTENT)]
public OracleBlob Content { set; get; }
// INullable
public bool IsNull
{
get
{
return string.IsNullOrWhiteSpace(FileName) && Content == null;
}
}
// INullable
public static AttachedFile Null
{
get
{
return new AttachedFile();
}
}
// IOracleCustomType
public void ToCustomObject(OracleConnection conn, IntPtr pUdt)
{
FileName = OracleUdt.GetValue(conn, pUdt, KEY_FILE_NAME) as string;
Content = OracleUdt.GetValue(conn, pUdt, KEY_CONTENT) as OracleBlob;
}
// IOracleCustomType
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, KEY_FILE_NAME, FileName);
OracleUdt.SetValue(con, pUdt, KEY_CONTENT, Content);
}
// Object
public override string ToString()
{
return FileName + ":" + Content;
}
}
/// <summary>
/// ODPNET.ATTACHED_FILE ユーザー定義オブジェクト型のマッピング定義.
/// </summary>
[OracleCustomTypeMapping("ODPNET.ATTACHED_FILE")]
public class AttachedFileFactory : IOracleCustomTypeFactory
{
// IOracleCustomTypeFactory
public IOracleCustomType CreateObject()
{
return new AttachedFile();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment