commit 2b7b8a6a7e94f2c8e8f16bc54ced6fab461762d6 Author: Yu Qing Date: Sun Dec 7 11:24:05 2014 +0800 move to github diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/HISTORY b/src/HISTORY new file mode 100644 index 0000000..e134b45 --- /dev/null +++ b/src/HISTORY @@ -0,0 +1,127 @@ + +Version 1.25 2014-12-07 + * support connection stats since FastDFS Server v5.04 + +Version 1.24 2012-12-01 + * bug fixed: StorageClient.get_file_info support appender file and + slave file correctly + +Version 1.23 2012-11-18 + * query_file_info changed, you must + upgrade your FastDFS server to V4.03 or higher version + +Version 1.22 2012-10-06 + * group and storage stat both add 1 field, you must + upgrade your FastDFS server to V4.00 or higher version + +Version 1.21 2012-06-25 + * add exception class MyException + * class IniFileReader same as FastDHT java client + +Version 1.20 2012-05-27 + * appender file support truncate and modify operation + * storage stat add 6 fields, you must upgrade your + FastDFS server to V3.08 or higher version + +Version 1.19 2011-05-29 + * storage stat add field if_trunk_server, you must upgrade your + FastDFS server to V3.00 or higher version + * tracker or storage close no throw IOException (silence close) + +Version 1.18 2011-01-29 + * storage stat support total_upload_bytes, success_upload_bytes, + total_download_bytes and success_download_bytes etc. 18 fields + you must upgrade your FastDFS server to V2.08 or higher version + * StorageClient add functions: get_file_info and query_file_info, + StorageClient1 add functions: get_file_info1 and query_file_info1 + +Version 1.17 2011-01-09 + * slave file's prefix name can be empty, + you must upgrade your FastDFS server to V2.07 or higher version + * support list group stats and storage stats + * support delete storage server + * add monitor class: org.csource.fastdfs.test.Monitor + +Version 1.16 2010-10-17 + * upload file can get available storage server list of the group, + you must upgrade your FastDFS server to V2.01 or higher version + +Version 1.15 2010-08-07 + * protocol reponse command changed. you must upgrade your FastDFS server + to V2.00 or higher version + * upload file protocol changed, you must upgrade your FastDFS server + to V2.00 or higher version + +Version 1.14 2010-06-22 + * bug fixed: delete the new created local file when download_file to + local file fail + * support connect timeout for connecting, the network timeout used to + read and write + +Version 1.13 2010-05-30 + * org.csource.fastdfs.ProtoCommon add method genSlaveFilename + * support ACTIVE_TEST command. you must upgrade your FastDFS server + to V1.28 or higher version + +Version 1.12 2009-11-27 + * support uploading slave file + * file ext name change from 5 to 6, you must upgrade your FastDFS server + to V1.23 or higher version + +Version 1.11 2009-10-22 + * correct interface / class name from beginning with Dowload to Download + * auto reconnect with reused connection when IOException ocurs + * test classes move to package org.csource.fastdfs.test + * add classes org.csource.fastdfs.UploadStream and + org.csource.fastdfs.DownloadStream for streaming writing or reading + +Version 1.10 2009-10-04 + * support multi-tracker server group, you can make multi + org.csource.fastdfs.TrackerClient instances for each tracker server group + +Version 1.9 2009-10-01 + * add load test class: org.csource.fastdfs.TestLoad + * network IO performance enhancement + * upload file support callback for sending file content + +Version 1.8 2009-08-30 + * TrackerClient, in function getConnection, add synchronized to avoid conflict + * support file URL with token + +Version 1.7 2009-08-01 + * support cmd TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL: query all storage + servers, you must upgrade your FastDFS server to V1.19 or higher version + +Version 1.6 2009-05-24 + * download file support offset and download bytes, you must upgrade your + FastDFS server to V1.18 or higher version + +Version 1.5 2009-02-14 + * client can specify group name when upload file, you must upgrade your + FastDFS server to V1.16 or higher version + +Version 1.4 2008-11-10 + * upload file support ext name, you must upgrade your + FastDFS server to V1.12 or higher version + * upload protocol add field store_path_index (1 byte) + * add method TrackerClient.getUpdateStorage. to support getmeta, setmeta + and delete file + * TrackerClient.getFetchStorage bug fixed + +Version 1.3 2008-10-16 + * client download file support callback function(class) + * add class "StorageClient1" and function "TrackerClient.getFetchStorage1" + to combine 2 fields: group name and filename to 1 field: file id + * add anothor client test program: TestClient1.java to use file id + +Version 1.2 2008-09-08 + * communication protocol changed to support large file exceed 2GB: + all integer field is 8 bytes big-endian, + you must upgrade your FastDFS server to V1.8 or higher version + +Version 1.1 2008-08-10 + * upload file package remove the one pad byte field, you must upgrade your + FastDFS server to V1.4 or higher version + +Version 1.0 2008-08-03 + * first version diff --git a/src/README b/src/README new file mode 100644 index 0000000..2ca67fc --- /dev/null +++ b/src/README @@ -0,0 +1,27 @@ +Copyright (C) 2008 Happy Fish / YuQing + +FastDFS Java Client API may be copied only under the terms of +the BSD license. +Please visit the FastDFS Home Page for more detail. +English language: http://english.csource.org/ +Chinese language: http://www.csource.org/ + + +The jar file is compiled by JDK1.5, you can download the last version +from google code: http://code.google.com/p/fastdfs/downloads/list + +run the FastDFS Java Client test: +java -cp org.csource.fastdfs.test.TestClient + +eg.: +java -cp fastdfs_client_v1.22.jar org.csource.fastdfs.test.TestClient fdfs_client.conf c:\windows\system32\notepad.exe + +or: +java -cp fastdfs_client_v1.22.jar org.csource.fastdfs.test.TestClient fdfs_client.conf /usr/include/stdlib.h + + +run the FastDFS monitor: +java -cp org.csource.fastdfs.test.Monitor + +eg.: +java -cp fastdfs_client_v1.22.jar org.csource.fastdfs.test.Monitor fdfs_client.conf diff --git a/src/build.xml b/src/build.xml new file mode 100644 index 0000000..23e2fb2 --- /dev/null +++ b/src/build.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fdfs_client.conf b/src/fdfs_client.conf new file mode 100644 index 0000000..84b6b50 --- /dev/null +++ b/src/fdfs_client.conf @@ -0,0 +1,9 @@ +connect_timeout = 2 +network_timeout = 30 +charset = ISO8859-1 +http.tracker_http_port = 8080 +http.anti_steal_token = no +http.secret_key = FastDFS1234567890 + +tracker_server = 192.168.0.116:22122 +tracker_server = 192.168.0.119:22122 diff --git a/src/org/csource/common/Base64.java b/src/org/csource/common/Base64.java new file mode 100644 index 0000000..937481b --- /dev/null +++ b/src/org/csource/common/Base64.java @@ -0,0 +1,539 @@ +package org.csource.common; + +import java.io.IOException; + +/** + * Freeware from: + * Roedy Green + * Canadian Mind Products + * #327 - 964 Heywood Avenue + * Victoria, BC Canada V8V 2Y5 + * tel:(250) 361-9093 + * mailto:roedy@mindprod.com + */ + +/** + * Encode arbitrary binary into printable ASCII using BASE64 encoding. + * very loosely based on the Base64 Reader by + * Dr. Mark Thornton + * Optrak Distribution Software Ltd. + * http://www.optrak.co.uk + * and Kevin Kelley's http://www.ruralnet.net/~kelley/java/Base64.java + * + * Base64 is a way of encoding 8-bit characters using only ASCII printable + * characters similar to UUENCODE. UUENCODE includes a filename where BASE64 does not. + * The spec is described in RFC 2045. Base64 is a scheme where + * 3 bytes are concatenated, then split to form 4 groups of 6-bits each; and + * each 6-bits gets translated to an encoded printable ASCII character, via a + * table lookup. An encoded string is therefore longer than the original by + * about 1/3. The "=" character is used to pad the end. Base64 is used, + * among other things, to encode the user:password string in an + * Authorization: header for HTTP. Don't confuse Base64 with + * x-www-form-urlencoded which is handled by + * Java.net.URLEncoder.encode/decode + * If you don't like this code, there is another implementation at http://www.ruffboy.com/download.htm + * Sun has an undocumented method called sun.misc.Base64Encoder.encode. + * You could use hex, simpler to code, but not as compact. + * + * If you wanted to encode a giant file, you could do it in large chunks that + * are even multiples of 3 bytes, except for the last chunk, and append the outputs. + * + * To encode a string, rather than binary data java.net.URLEncoder may be better. See + * printable characters in the Java glossary for a discussion of the differences. + * + * version 1.4 2002 February 15 -- correct bugs with uneven line lengths, + * allow you to configure line separator. + * now need Base64 object and instance methods. + * new mailing address. + * version 1.3 2000 September 12 -- fix problems with estimating output length in encode + * version 1.2 2000 September 09 -- now handles decode as well. + * version 1.1 1999 December 04 -- more symmetrical encoding algorithm. + * more accurate StringBuffer allocation size. + * version 1.0 1999 December 03 -- posted in comp.lang.java.programmer. + * Futures Streams or files. + */ + +public class Base64 +{ + + /** + * how we separate lines, e.g. \n, \r\n, \r etc. + */ + private String lineSeparator = System.getProperty( "line.separator" ); + + /** + * max chars per line, excluding lineSeparator. A multiple of 4. + */ + private int lineLength = 72; + + private char[] valueToChar = new char[64]; + + /** + * binary value encoded by a given letter of the alphabet 0..63 + */ + private int[] charToValue = new int[256]; + + private int[] charToPad = new int[4]; + + /* constructor */ + public Base64() + { + this.init('+', '/', '='); + } + + /* constructor */ + public Base64(char chPlus, char chSplash, char chPad, int lineLength) + { + this.init(chPlus, chSplash, chPad); + this.lineLength = lineLength; + } + + public Base64(int lineLength) + { + this.lineLength = lineLength; + } + + /* initialise defaultValueToChar and defaultCharToValue tables */ + private void init(char chPlus, char chSplash, char chPad) + { + int index = 0; + // build translate this.valueToChar table only once. + // 0..25 -> 'A'..'Z' + for ( int i='A'; i<='Z'; i++) { + this.valueToChar[index++] = (char)i; + } + + // 26..51 -> 'a'..'z' + for ( int i='a'; i<='z'; i++ ) { + this.valueToChar[index++] = (char)i; + } + + // 52..61 -> '0'..'9' + for ( int i='0'; i<='9'; i++) { + this.valueToChar[index++] = (char)i; + } + + this.valueToChar[index++] = chPlus; + this.valueToChar[index++] = chSplash; + + // build translate defaultCharToValue table only once. + for ( int i=0; i<256; i++ ) + { + this.charToValue[i] = IGNORE; // default is to ignore + } + + for ( int i=0; i<64; i++ ) + { + this.charToValue[this.valueToChar[i]] = i; + } + + this.charToValue[chPad] = PAD; + java.util.Arrays.fill(this.charToPad, chPad); + } + + /** + * Encode an arbitrary array of bytes as Base64 printable ASCII. + * It will be broken into lines of 72 chars each. The last line is not + * terminated with a line separator. + * The output will always have an even multiple of data characters, + * exclusive of \n. It is padded out with =. + */ + public String encode(byte[] b) throws IOException + { + // Each group or partial group of 3 bytes becomes four chars + // covered quotient + int outputLength = ((b.length + 2) / 3) * 4; + + // account for trailing newlines, on all but the very last line + if ( lineLength != 0 ) + { + int lines = ( outputLength + lineLength -1 ) / lineLength - 1; + if ( lines > 0 ) + { + outputLength += lines * lineSeparator.length(); + } + } + + // must be local for recursion to work. + StringBuffer sb = new StringBuffer( outputLength ); + + // must be local for recursion to work. + int linePos = 0; + + // first deal with even multiples of 3 bytes. + int len = (b.length / 3) * 3; + int leftover = b.length - len; + for ( int i=0; i lineLength ) + { + if ( lineLength != 0 ) + { + sb.append(lineSeparator); + } + linePos = 4; + } + + // get next three bytes in unsigned form lined up, + // in big-endian order + int combined = b[i+0] & 0xff; + combined <<= 8; + combined |= b[i+1] & 0xff; + combined <<= 8; + combined |= b[i+2] & 0xff; + + // break those 24 bits into a 4 groups of 6 bits, + // working LSB to MSB. + int c3 = combined & 0x3f; + combined >>>= 6; + int c2 = combined & 0x3f; + combined >>>= 6; + int c1 = combined & 0x3f; + combined >>>= 6; + int c0 = combined & 0x3f; + + // Translate into the equivalent alpha character + // emitting them in big-endian order. + sb.append( valueToChar[c0]); + sb.append( valueToChar[c1]); + sb.append( valueToChar[c2]); + sb.append( valueToChar[c3]); + } + + // deal with leftover bytes + switch ( leftover ) + { + case 0: + default: + // nothing to do + break; + + case 1: + // One leftover byte generates xx== + // Start a new line if next 4 chars won't fit on the current line + linePos += 4; + if ( linePos > lineLength ) + { + + if ( lineLength != 0 ) + { + sb.append(lineSeparator); + } + linePos = 4; + } + + // Handle this recursively with a faked complete triple. + // Throw away last two chars and replace with == + sb.append(encode(new byte[] {b[len], 0, 0} + ).substring(0,2)); + sb.append("=="); + break; + + case 2: + // Two leftover bytes generates xxx= + // Start a new line if next 4 chars won't fit on the current line + linePos += 4; + if ( linePos > lineLength ) + { + if ( lineLength != 0 ) + { + sb.append(lineSeparator); + } + linePos = 4; + } + // Handle this recursively with a faked complete triple. + // Throw away last char and replace with = + sb.append(encode(new byte[] {b[len], b[len+1], 0} + ).substring(0,3)); + sb.append("="); + break; + + } // end switch; + + if ( outputLength != sb.length() ) + { + System.out.println("oops: minor program flaw: output length mis-estimated"); + System.out.println("estimate:" + outputLength); + System.out.println("actual:" + sb.length()); + } + return sb.toString(); + }// end encode + + /** + * decode a well-formed complete Base64 string back into an array of bytes. + * It must have an even multiple of 4 data characters (not counting \n), + * padded out with = as needed. + */ + public byte[] decodeAuto( String s) { + int nRemain = s.length() % 4; + if (nRemain == 0) { + return this.decode(s); + } else { + return this.decode(s + new String(this.charToPad, 0, 4 - nRemain)); + } + } + + /** + * decode a well-formed complete Base64 string back into an array of bytes. + * It must have an even multiple of 4 data characters (not counting \n), + * padded out with = as needed. + */ + public byte[] decode( String s) + { + + // estimate worst case size of output array, no embedded newlines. + byte[] b = new byte[(s.length() / 4) * 3]; + + // tracks where we are in a cycle of 4 input chars. + int cycle = 0; + + // where we combine 4 groups of 6 bits and take apart as 3 groups of 8. + int combined = 0; + + // how many bytes we have prepared. + int j = 0; + // will be an even multiple of 4 chars, plus some embedded \n + int len = s.length(); + int dummies = 0; + for ( int i=0; i>>= 8; + b[j+1] = (byte)combined; + combined >>>= 8; + b[j] = (byte)combined; + j += 3; + cycle = 0; + break; + } + break; + } + } // end for + if ( cycle != 0 ) + { + throw new ArrayIndexOutOfBoundsException ("Input to decode not an even multiple of 4 characters; pad with =."); + } + j -= dummies; + if ( b.length != j ) + { + byte[] b2 = new byte[j]; + System.arraycopy(b, 0, b2, 0, j); + b = b2; + } + return b; + + }// end decode + + /** + * determines how long the lines are that are generated by encode. + * Ignored by decode. + * @param length 0 means no newlines inserted. Must be a multiple of 4. + */ + public void setLineLength(int length) + { + this.lineLength = (length/4) * 4; + } + + /** + * How lines are separated. + * Ignored by decode. + * @param lineSeparator may be "" but not null. + * Usually contains only a combination of chars \n and \r. + * Could be any chars not in set A-Z a-z 0-9 + /. + */ + public void setLineSeparator(String lineSeparator) + { + this.lineSeparator = lineSeparator; + } + + /** + * Marker value for chars we just ignore, e.g. \n \r high ascii + */ + static final int IGNORE = -1; + + /** + * Marker for = trailing pad + */ + static final int PAD = -2; + + /** + * used to disable test driver + */ + private static final boolean debug = true; + + /** + * debug display array + */ + public static void show (byte[] b) + { + int count = 0; + int rows = 0; + + + for ( int i=0; i= 0 && pkg_len != expect_body_len) + { + throw new IOException("recv body length: " + pkg_len + " is not correct, expect length: " + expect_body_len); + } + + return new RecvHeaderInfo((byte)0, pkg_len); + } + +/** +* receive whole pack +* @param in input stream +* @param expect_cmd expect response command +* @param expect_body_len expect response package body length +* @return RecvPackageInfo: errno and reponse body(byte buff) +*/ + public static RecvPackageInfo recvPackage(InputStream in, byte expect_cmd, long expect_body_len) throws IOException + { + RecvHeaderInfo header = recvHeader(in, expect_cmd, expect_body_len); + if (header.errno != 0) + { + return new RecvPackageInfo(header.errno, null); + } + + byte[] body = new byte[(int)header.body_len]; + int totalBytes = 0; + int remainBytes = (int)header.body_len; + int bytes; + + while (totalBytes < header.body_len) + { + if ((bytes=in.read(body, totalBytes, remainBytes)) < 0) + { + break; + } + + totalBytes += bytes; + remainBytes -= bytes; + } + + if (totalBytes != header.body_len) + { + throw new IOException("recv package size " + totalBytes + " != " + header.body_len); + } + + return new RecvPackageInfo((byte)0, body); + } + +/** +* split metadata to name value pair array +* @param meta_buff metadata +* @return name value pair array +*/ + public static NameValuePair[] split_metadata(String meta_buff) + { + return split_metadata(meta_buff, FDFS_RECORD_SEPERATOR, FDFS_FIELD_SEPERATOR); + } + +/** +* split metadata to name value pair array +* @param meta_buff metadata +* @param recordSeperator record/row seperator +* @param filedSeperator field/column seperator +* @return name value pair array +*/ + public static NameValuePair[] split_metadata(String meta_buff, + String recordSeperator, String filedSeperator) + { + String[] rows; + String[] cols; + NameValuePair[] meta_list; + + rows = meta_buff.split(recordSeperator); + meta_list = new NameValuePair[rows.length]; + for (int i=0; i> 56) & 0xFF); + bs[1] = (byte)((n >> 48) & 0xFF); + bs[2] = (byte)((n >> 40) & 0xFF); + bs[3] = (byte)((n >> 32) & 0xFF); + bs[4] = (byte)((n >> 24) & 0xFF); + bs[5] = (byte)((n >> 16) & 0xFF); + bs[6] = (byte)((n >> 8) & 0xFF); + bs[7] = (byte)(n & 0xFF); + + return bs; + } + +/** +* buff convert to long +* @param bs the buffer (big-endian) +* @param offset the start position based 0 +* @return long number +*/ + public static long buff2long(byte[] bs, int offset) + { + return (((long)(bs[offset] >= 0 ? bs[offset] : 256+bs[offset])) << 56) | + (((long)(bs[offset+1] >= 0 ? bs[offset+1] : 256+bs[offset+1])) << 48) | + (((long)(bs[offset+2] >= 0 ? bs[offset+2] : 256+bs[offset+2])) << 40) | + (((long)(bs[offset+3] >= 0 ? bs[offset+3] : 256+bs[offset+3])) << 32) | + (((long)(bs[offset+4] >= 0 ? bs[offset+4] : 256+bs[offset+4])) << 24) | + (((long)(bs[offset+5] >= 0 ? bs[offset+5] : 256+bs[offset+5])) << 16) | + (((long)(bs[offset+6] >= 0 ? bs[offset+6] : 256+bs[offset+6])) << 8) | + ((long)(bs[offset+7] >= 0 ? bs[offset+7] : 256+bs[offset+7])); + } + +/** +* buff convert to int +* @param bs the buffer (big-endian) +* @param offset the start position based 0 +* @return int number +*/ + public static int buff2int(byte[] bs, int offset) + { + return (((int)(bs[offset] >= 0 ? bs[offset] : 256+bs[offset])) << 24) | + (((int)(bs[offset+1] >= 0 ? bs[offset+1] : 256+bs[offset+1])) << 16) | + (((int)(bs[offset+2] >= 0 ? bs[offset+2] : 256+bs[offset+2])) << 8) | + ((int)(bs[offset+3] >= 0 ? bs[offset+3] : 256+bs[offset+3])); + } + +/** +* buff convert to ip address +* @param bs the buffer (big-endian) +* @param offset the start position based 0 +* @return ip address +*/ + public static String getIpAddress(byte[] bs, int offset) + { + if (bs[0] == 0 || bs[3] == 0) //storage server ID + { + return ""; + } + + int n; + StringBuilder sbResult = new StringBuilder(16); + for (int i=offset; i= 0) ? bs[i] : 256 + bs[i]; + if (sbResult.length() > 0) + { + sbResult.append("."); + } + sbResult.append(String.valueOf(n)); + } + + return sbResult.toString(); + } + + /** +* md5 function +* @param source the input buffer +* @return md5 string +*/ + public static String md5(byte[] source) throws NoSuchAlgorithmException + { + char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); + md.update(source); + byte tmp[] = md.digest(); + char str[] = new char[32]; + int k = 0; + for (int i = 0; i < 16; i++) + { + str[k++] = hexDigits[tmp[i] >>> 4 & 0xf]; + str[k++] = hexDigits[tmp[i] & 0xf]; + } + + return new String(str); + } + + /** +* get token for file URL +* @param remote_filename the filename return by FastDFS server +* @param ts unix timestamp, unit: second +* @param secret_key the secret key +* @return token string +*/ + public static String getToken(String remote_filename, int ts, String secret_key) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException + { + byte[] bsFilename = remote_filename.getBytes(ClientGlobal.g_charset); + byte[] bsKey = secret_key.getBytes(ClientGlobal.g_charset); + byte[] bsTimestamp = (new Integer(ts)).toString().getBytes(ClientGlobal.g_charset); + + byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length]; + System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length); + System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length); + System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length); + + return md5(buff); + } + + /** +* generate slave filename +* @param master_filename the master filename to generate the slave filename +* @param prefix_name the prefix name to generate the slave filename +* @param ext_name the extension name of slave filename, null for same as the master extension name +* @return slave filename string +*/ + public static String genSlaveFilename(String master_filename, + String prefix_name, String ext_name) throws MyException + { + String true_ext_name; + int dotIndex; + + if (master_filename.length() < 28 + FDFS_FILE_EXT_NAME_MAX_LEN) + { + throw new MyException("master filename \"" + master_filename + "\" is invalid"); + } + + dotIndex = master_filename.indexOf('.', master_filename.length() - (FDFS_FILE_EXT_NAME_MAX_LEN + 1)); + if (ext_name != null) + { + if (ext_name.length() == 0) + { + true_ext_name = ""; + } + else if (ext_name.charAt(0) == '.') + { + true_ext_name = ext_name; + } + else + { + true_ext_name = "." + ext_name; + } + } + else + { + if (dotIndex < 0) + { + true_ext_name = ""; + } + else + { + true_ext_name = master_filename.substring(dotIndex); + } + } + + if (true_ext_name.length() == 0 && prefix_name.equals("-m")) + { + throw new MyException("prefix_name \"" + prefix_name + "\" is invalid"); + } + + if (dotIndex < 0) + { + return master_filename + prefix_name + true_ext_name; + } + else + { + return master_filename.substring(0, dotIndex) + prefix_name + true_ext_name; + } + } +} diff --git a/src/org/csource/fastdfs/ProtoStructDecoder.java b/src/org/csource/fastdfs/ProtoStructDecoder.java new file mode 100644 index 0000000..c30cb6a --- /dev/null +++ b/src/org/csource/fastdfs/ProtoStructDecoder.java @@ -0,0 +1,55 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +import java.io.*; +import java.util.*; +import java.net.*; +import java.lang.reflect.Array; +import org.csource.common.*; + +/** +* C struct body decoder +* @author Happy Fish / YuQing +* @version Version 1.17 +*/ +public class ProtoStructDecoder +{ +/** +* Constructor +*/ + public ProtoStructDecoder() + { + } + +/** +* decode byte buffer +*/ + public T[] decode(byte[] bs, Class clazz, int fieldsTotalSize) throws Exception + { + if (bs.length % fieldsTotalSize != 0) + { + throw new IOException("byte array length: " + bs.length + " is invalid!"); + } + + int count = bs.length / fieldsTotalSize; + int offset; + T[] results = (T[])Array.newInstance(clazz, count); + + offset = 0; + for (int i=0; i + *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final String group_name = null; + return this.upload_file(group_name, local_filename, file_ext_name, meta_list); + } + + /** + * upload file to storage server (by file name) + * @param group_name the group name to upload file to, can be empty + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + protected String[] upload_file(String group_name, String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE; + return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list); + } + + /** + * upload file to storage server (by file name) + * @param cmd the command + * @param group_name the group name to upload file to, can be empty + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + protected String[] upload_file(byte cmd, String group_name, String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + File f = new File(local_filename); + FileInputStream fis = new FileInputStream(f); + + if (file_ext_name == null) + { + int nPos = local_filename.lastIndexOf('.'); + if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + file_ext_name = local_filename.substring(nPos+1); + } + } + + try + { + return this.do_upload_file(cmd, group_name, null, null, file_ext_name, + f.length(), new UploadStream(fis, f.length()), meta_list); + } + finally + { + fis.close(); + } + } + + /** + * upload file to storage server (by file buff) + * @param file_buff file content/buff + * @param offset start offset of the buff + * @param length the length of buff to upload + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(byte[] file_buff, int offset, int length, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final String group_name = null; + return this.upload_file(group_name, file_buff, offset, length, file_ext_name, meta_list); + } + + /** + * upload file to storage server (by file buff) + * @param group_name the group name to upload file to, can be empty + * @param file_buff file content/buff + * @param offset start offset of the buff + * @param length the length of buff to upload + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, byte[] file_buff, int offset, int length, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name, + length, new UploadBuff(file_buff, offset, length), meta_list); + } + + /** + * upload file to storage server (by file buff) + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(byte[] file_buff, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final String group_name = null; + return this.upload_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list); + } + + /** + * upload file to storage server (by file buff) + * @param group_name the group name to upload file to, can be empty + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, byte[] file_buff, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name, + file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list); + } + + + /** + * upload file to storage server (by callback) + * @param group_name the group name to upload file to, can be empty + * @param file_size the file size + * @param callback the write data callback object + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, long file_size, UploadCallback callback, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + final String master_filename = null; + final String prefix_name = null; + + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, master_filename, prefix_name, + file_ext_name, file_size, callback, meta_list); + } + + /** + * upload file to storage server (by file name, slave file mode) + * @param group_name the group name of master file + * @param master_filename the master file name to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, String master_filename, String prefix_name, + String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + if ((group_name == null || group_name.length() == 0) || + (master_filename == null || master_filename.length() == 0) || + (prefix_name == null)) + { + throw new MyException("invalid arguement"); + } + + File f = new File(local_filename); + FileInputStream fis = new FileInputStream(f); + + if (file_ext_name == null) + { + int nPos = local_filename.lastIndexOf('.'); + if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + file_ext_name = local_filename.substring(nPos+1); + } + } + + try + { + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, + file_ext_name, f.length(), new UploadStream(fis, f.length()), meta_list); + } + finally + { + fis.close(); + } + } + + /** + * upload file to storage server (by file buff, slave file mode) + * @param group_name the group name of master file + * @param master_filename the master file name to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, String master_filename, String prefix_name, + byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + if ((group_name == null || group_name.length() == 0) || + (master_filename == null || master_filename.length() == 0) || + (prefix_name == null)) + { + throw new MyException("invalid arguement"); + } + + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, + file_ext_name, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list); + } + + /** + * upload file to storage server (by file buff, slave file mode) + * @param group_name the group name of master file + * @param master_filename the master file name to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_buff file content/buff + * @param offset start offset of the buff + * @param length the length of buff to upload + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, String master_filename, String prefix_name, + byte[] file_buff, int offset, int length, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + if ((group_name == null || group_name.length() == 0) || + (master_filename == null || master_filename.length() == 0) || + (prefix_name == null)) + { + throw new MyException("invalid arguement"); + } + + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, + file_ext_name, length, new UploadBuff(file_buff, offset, length), meta_list); + } + + /** + * upload file to storage server (by callback, slave file mode) + * @param group_name the group name to upload file to, can be empty + * @param master_filename the master file name to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_size the file size + * @param callback the write data callback object + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_file(String group_name, String master_filename, + String prefix_name, long file_size, UploadCallback callback, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name, + file_ext_name, file_size, callback, meta_list); + } + + /** + * upload appender file to storage server (by file name) + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_appender_file(String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final String group_name = null; + return this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list); + } + + /** + * upload appender file to storage server (by file name) + * @param group_name the group name to upload file to, can be empty + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + protected String[] upload_appender_file(String group_name, String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE; + return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list); + } + + /** + * upload appender file to storage server (by file buff) + * @param file_buff file content/buff + * @param offset start offset of the buff + * @param length the length of buff to upload + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_appender_file(byte[] file_buff, int offset, int length, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final String group_name = null; + return this.upload_appender_file(group_name, file_buff, offset, length, file_ext_name, meta_list); + } + + /** + * upload appender file to storage server (by file buff) + * @param group_name the group name to upload file to, can be empty + * @param file_buff file content/buff + * @param offset start offset of the buff + * @param length the length of buff to upload + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_appender_file(String group_name, byte[] file_buff, int offset, int length, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name, + length, new UploadBuff(file_buff, offset, length), meta_list); + } + + /** + * upload appender file to storage server (by file buff) + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_appender_file(byte[] file_buff, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + final String group_name = null; + return this.upload_appender_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list); + } + + /** + * upload appender file to storage server (by file buff) + * @param group_name the group name to upload file to, can be empty + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_appender_file(String group_name, byte[] file_buff, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name, + file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list); + } + + /** + * upload appender file to storage server (by callback) + * @param group_name the group name to upload file to, can be empty + * @param file_size the file size + * @param callback the write data callback object + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] upload_appender_file(String group_name, long file_size, UploadCallback callback, + String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + final String master_filename = null; + final String prefix_name = null; + + return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, master_filename, prefix_name, + file_ext_name, file_size, callback, meta_list); + } + + /** + * append file to storage server (by file name) + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param local_filename local filename to append + * @return 0 for success, != 0 for error (error no) + */ + public int append_file(String group_name, String appender_filename, String local_filename) throws IOException, MyException + { + File f = new File(local_filename); + FileInputStream fis = new FileInputStream(f); + + try + { + return this.do_append_file(group_name, appender_filename, f.length(), new UploadStream(fis, f.length())); + } + finally + { + fis.close(); + } + } + + /** + * append file to storage server (by file buff) + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_buff file content/buff + * @return 0 for success, != 0 for error (error no) + */ + public int append_file(String group_name, String appender_filename, byte[] file_buff) throws IOException, MyException + { + return this.do_append_file(group_name, appender_filename, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length)); + } + + /** + * append file to storage server (by file buff) + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_buff file content/buff + * @param offset start offset of the buff + * @param length the length of buff to append + * @return 0 for success, != 0 for error (error no) + */ + public int append_file(String group_name, String appender_filename, + byte[] file_buff, int offset, int length) throws IOException, MyException + { + return this.do_append_file(group_name, appender_filename, length, new UploadBuff(file_buff, offset, length)); + } + + /** + * append file to storage server (by callback) + * @param group_name the group name to append file to + * @param appender_filename the appender filename + * @param file_size the file size + * @param callback the write data callback object + * @return 0 for success, != 0 for error (error no) + */ + public int append_file(String group_name, String appender_filename, + long file_size, UploadCallback callback) throws IOException, MyException + { + return this.do_append_file(group_name, appender_filename, file_size, callback); + } + + /** + * modify appender file to storage server (by file name) + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_offset the offset of appender file + * @param local_filename local filename to append + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file(String group_name, String appender_filename, + long file_offset, String local_filename) throws IOException, MyException + { + File f = new File(local_filename); + FileInputStream fis = new FileInputStream(f); + + try + { + return this.do_modify_file(group_name, appender_filename, file_offset, + f.length(), new UploadStream(fis, f.length())); + } + finally + { + fis.close(); + } + } + + /** + * modify appender file to storage server (by file buff) + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_offset the offset of appender file + * @param file_buff file content/buff + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file(String group_name, String appender_filename, + long file_offset, byte[] file_buff) throws IOException, MyException + { + return this.do_modify_file(group_name, appender_filename, file_offset, + file_buff.length, new UploadBuff(file_buff, 0, file_buff.length)); + } + + /** + * modify appender file to storage server (by file buff) + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_offset the offset of appender file + * @param file_buff file content/buff + * @param buffer_offset start offset of the buff + * @param buffer_length the length of buff to modify + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file(String group_name, String appender_filename, + long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, MyException + { + return this.do_modify_file(group_name, appender_filename, file_offset, + buffer_length, new UploadBuff(file_buff, buffer_offset, buffer_length)); + } + + /** + * modify appender file to storage server (by callback) + * @param group_name the group name to modify file to + * @param appender_filename the appender filename + * @param file_offset the offset of appender file + * @param modify_size the modify size + * @param callback the write data callback object + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file(String group_name, String appender_filename, + long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException + { + return this.do_modify_file(group_name, appender_filename, file_offset, + modify_size, callback); + } + + /** + * upload file to storage server + * @param cmd the command code + * @param group_name the group name to upload file to, can be empty + * @param master_filename the master file name to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_ext_name file ext name, do not include dot(.) + * @param file_size the file size + * @param callback the write data callback object + * @param meta_list meta info array + * @return 2 elements string array if success:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + protected String[] do_upload_file(byte cmd, String group_name, String master_filename, + String prefix_name, String file_ext_name, long file_size, UploadCallback callback, + NameValuePair[] meta_list) throws IOException, MyException + { + byte[] header; + byte[] ext_name_bs; + String new_group_name; + String remote_filename; + boolean bNewConnection; + Socket storageSocket; + byte[] sizeBytes; + byte[] hexLenBytes; + byte[] masterFilenameBytes; + boolean bUploadSlave; + int offset; + long body_len; + + bUploadSlave = ((group_name != null && group_name.length() > 0) && + (master_filename != null && master_filename.length() > 0) && + (prefix_name != null)); + if (bUploadSlave) + { + bNewConnection = this.newUpdatableStorageConnection(group_name, master_filename); + } + else + { + bNewConnection = this.newWritableStorageConnection(group_name); + } + + try + { + storageSocket = this.storageServer.getSocket(); + + ext_name_bs = new byte[ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN]; + Arrays.fill(ext_name_bs, (byte)0); + if (file_ext_name != null && file_ext_name.length() > 0) + { + byte[] bs = file_ext_name.getBytes(ClientGlobal.g_charset); + int ext_name_len = bs.length; + if (ext_name_len > ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN) + { + ext_name_len = ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN; + } + System.arraycopy(bs, 0, ext_name_bs, 0, ext_name_len); + } + + if (bUploadSlave) + { + masterFilenameBytes = master_filename.getBytes(ClientGlobal.g_charset); + + sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; + body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + + masterFilenameBytes.length + file_size; + + hexLenBytes = ProtoCommon.long2buff(master_filename.length()); + System.arraycopy(hexLenBytes, 0, sizeBytes, 0, hexLenBytes.length); + offset = hexLenBytes.length; + } + else + { + masterFilenameBytes = null; + sizeBytes = new byte[1 + 1 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; + body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + file_size; + + sizeBytes[0] = (byte)this.storageServer.getStorePathIndex(); + offset = 1; + } + + hexLenBytes = ProtoCommon.long2buff(file_size); + System.arraycopy(hexLenBytes, 0, sizeBytes, offset, hexLenBytes.length); + + OutputStream out = storageSocket.getOutputStream(); + header = ProtoCommon.packHeader(cmd, body_len, (byte)0); + byte[] wholePkg = new byte[(int)(header.length + body_len - file_size)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length); + offset = header.length + sizeBytes.length; + if (bUploadSlave) + { + byte[] prefix_name_bs = new byte[ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN]; + byte[] bs = prefix_name.getBytes(ClientGlobal.g_charset); + int prefix_name_len = bs.length; + Arrays.fill(prefix_name_bs, (byte)0); + if (prefix_name_len > ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN) + { + prefix_name_len = ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN; + } + if (prefix_name_len > 0) + { + System.arraycopy(bs, 0, prefix_name_bs, 0, prefix_name_len); + } + + System.arraycopy(prefix_name_bs, 0, wholePkg, offset, prefix_name_bs.length); + offset += prefix_name_bs.length; + } + + System.arraycopy(ext_name_bs, 0, wholePkg, offset, ext_name_bs.length); + offset += ext_name_bs.length; + + if (bUploadSlave) + { + System.arraycopy(masterFilenameBytes, 0, wholePkg, offset, masterFilenameBytes.length); + offset += masterFilenameBytes.length; + } + + out.write(wholePkg); + + if ((this.errno=(byte)callback.send(out)) != 0) + { + return null; + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + if (pkgInfo.body.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) + { + throw new MyException("body length: " + pkgInfo.body.length + " <= " + ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); + } + + new_group_name = new String(pkgInfo.body, 0, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN).trim(); + remote_filename = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, pkgInfo.body.length - ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); + String[] results = new String[2]; + results[0] = new_group_name; + results[1] = remote_filename; + + if (meta_list == null || meta_list.length == 0) + { + return results; + } + + int result = 0; + try + { + result = this.set_metadata(new_group_name, remote_filename, + meta_list, ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE); + } + catch(IOException ex) + { + result = 5; + throw ex; + } + finally + { + if (result != 0) + { + this.errno = (byte)result; + this.delete_file(new_group_name, remote_filename); + return null; + } + } + + return results; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * append file to storage server + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_size the file size + * @param callback the write data callback object + * @return return true for success, false for fail + */ + protected int do_append_file(String group_name, String appender_filename, + long file_size, UploadCallback callback) throws IOException, MyException + { + byte[] header; + boolean bNewConnection; + Socket storageSocket; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + long body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) + { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return this.errno; + } + + bNewConnection = this.newUpdatableStorageConnection(group_name, appender_filename); + + try + { + storageSocket = this.storageServer.getSocket(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + file_size; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_APPEND_FILE, body_len, (byte)0); + byte[] wholePkg = new byte[(int)(header.length + body_len - file_size)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(file_size); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + OutputStream out = storageSocket.getOutputStream(); + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + out.write(wholePkg); + if ((this.errno=(byte)callback.send(out)) != 0) + { + return this.errno; + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return this.errno; + } + + return 0; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * modify appender file to storage server + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_offset the offset of appender file + * @param modify_size the modify size + * @param callback the write data callback object + * @return return true for success, false for fail + */ + protected int do_modify_file(String group_name, String appender_filename, + long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException + { + byte[] header; + boolean bNewConnection; + Socket storageSocket; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + long body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) + { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return this.errno; + } + + bNewConnection = this.newUpdatableStorageConnection(group_name, appender_filename); + + try + { + storageSocket = this.storageServer.getSocket(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + modify_size; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_MODIFY_FILE, body_len, (byte)0); + byte[] wholePkg = new byte[(int)(header.length + body_len - modify_size)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(file_offset); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(modify_size); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + OutputStream out = storageSocket.getOutputStream(); + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + out.write(wholePkg); + if ((this.errno=(byte)callback.send(out)) != 0) + { + return this.errno; + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return this.errno; + } + + return 0; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * delete file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return 0 for success, none zero for fail (error code) + */ + public int delete_file(String group_name, String remote_filename) throws IOException, MyException + { + boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + + try + { + this.send_package(ProtoCommon.STORAGE_PROTO_CMD_DELETE_FILE, group_name, remote_filename); + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + + this.errno = pkgInfo.errno; + return pkgInfo.errno; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * truncate appender file to size 0 from storage server + * @param group_name the group name of storage server + * @param appender_filename the appender filename + * @return 0 for success, none zero for fail (error code) + */ + public int truncate_file(String group_name, String appender_filename) throws IOException, MyException + { + final long truncated_file_size = 0; + return this.truncate_file(group_name, appender_filename, truncated_file_size); + } + + /** + * truncate appender file from storage server + * @param group_name the group name of storage server + * @param appender_filename the appender filename + * @param truncated_file_size truncated file size + * @return 0 for success, none zero for fail (error code) + */ + public int truncate_file(String group_name, String appender_filename, + long truncated_file_size) throws IOException, MyException + { + byte[] header; + boolean bNewConnection; + Socket storageSocket; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + int body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) + { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return this.errno; + } + + bNewConnection = this.newUpdatableStorageConnection(group_name, appender_filename); + + try + { + storageSocket = this.storageServer.getSocket(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_TRUNCATE_FILE, body_len, (byte)0); + byte[] wholePkg = new byte[header.length + body_len]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(truncated_file_size); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + OutputStream out = storageSocket.getOutputStream(); + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + out.write(wholePkg); + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + return pkgInfo.errno; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * download file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return file content/buff, return null if fail + */ + public byte[] download_file(String group_name, String remote_filename) throws IOException, MyException + { + final long file_offset = 0; + final long download_bytes = 0; + + return this.download_file(group_name, remote_filename, file_offset, download_bytes); + } + + /** + * download file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param file_offset the start offset of the file + * @param download_bytes download bytes, 0 for remain bytes from offset + * @return file content/buff, return null if fail + */ + public byte[] download_file(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException, MyException + { + boolean bNewConnection = this.newReadableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + + try + { + ProtoCommon.RecvPackageInfo pkgInfo; + + this.send_download_package(group_name, remote_filename, file_offset, download_bytes); + pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + return pkgInfo.body; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * download file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param local_filename filename on local + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + String local_filename) throws IOException, MyException + { + final long file_offset = 0; + final long download_bytes = 0; + return this.download_file(group_name, remote_filename, + file_offset, download_bytes, local_filename); + } + + /** + * download file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param file_offset the start offset of the file + * @param download_bytes download bytes, 0 for remain bytes from offset + * @param local_filename filename on local + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + long file_offset, long download_bytes, + String local_filename) throws IOException, MyException + { + boolean bNewConnection = this.newReadableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + try + { + ProtoCommon.RecvHeaderInfo header; + FileOutputStream out = new FileOutputStream(local_filename); + try + { + this.errno = 0; + this.send_download_package(group_name, remote_filename, file_offset, download_bytes); + + InputStream in = storageSocket.getInputStream(); + header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = header.errno; + if (header.errno != 0) + { + return header.errno; + } + + byte[] buff = new byte[256 * 1024]; + long remainBytes = header.body_len; + int bytes; + + //System.out.println("expect_body_len=" + header.body_len); + + while (remainBytes > 0) + { + if ((bytes=in.read(buff, 0, remainBytes > buff.length ? buff.length : (int)remainBytes)) < 0) + { + throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len); + } + + out.write(buff, 0, bytes); + remainBytes -= bytes; + + //System.out.println("totalBytes=" + (header.body_len - remainBytes)); + } + + return 0; + } + catch(IOException ex) + { + if (this.errno == 0) + { + this.errno = ProtoCommon.ERR_NO_EIO; + } + + throw ex; + } + finally + { + out.close(); + if (this.errno != 0) + { + (new File(local_filename)).delete(); + } + } + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * download file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param callback call callback.recv() when data arrive + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + DownloadCallback callback) throws IOException, MyException + { + final long file_offset = 0; + final long download_bytes = 0; + return this.download_file(group_name, remote_filename, + file_offset, download_bytes, callback); + } + + /** + * download file from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param file_offset the start offset of the file + * @param download_bytes download bytes, 0 for remain bytes from offset + * @param callback call callback.recv() when data arrive + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + long file_offset, long download_bytes, + DownloadCallback callback) throws IOException, MyException + { + int result; + boolean bNewConnection = this.newReadableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + + try + { + ProtoCommon.RecvHeaderInfo header; + this.send_download_package(group_name, remote_filename, file_offset, download_bytes); + + InputStream in = storageSocket.getInputStream(); + header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = header.errno; + if (header.errno != 0) + { + return header.errno; + } + + byte[] buff = new byte[2 * 1024]; + long remainBytes = header.body_len; + int bytes; + + //System.out.println("expect_body_len=" + header.body_len); + + while (remainBytes > 0) + { + if ((bytes=in.read(buff, 0, remainBytes > buff.length ? buff.length : (int)remainBytes)) < 0) + { + throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len); + } + + if ((result=callback.recv(header.body_len, buff, bytes)) != 0) + { + this.errno = (byte)result; + return result; + } + + remainBytes -= bytes; + //System.out.println("totalBytes=" + (header.body_len - remainBytes)); + } + + return 0; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * get all metadata items from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return meta info array, return null if fail + */ + public NameValuePair[] get_metadata(String group_name, String remote_filename)throws IOException, MyException + { + boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + + try + { + ProtoCommon.RecvPackageInfo pkgInfo; + + this.send_package(ProtoCommon.STORAGE_PROTO_CMD_GET_METADATA, group_name, remote_filename); + pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + return ProtoCommon.split_metadata(new String(pkgInfo.body, ClientGlobal.g_charset)); + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * set metadata items to storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param meta_list meta item array + * @param op_flag flag, can be one of following values:
+ *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old + * metadata items
+ *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when + * the metadata item not exist, otherwise update it
+ * @return 0 for success, !=0 fail (error code) + */ + public int set_metadata(String group_name, String remote_filename, + NameValuePair[] meta_list, byte op_flag) throws IOException, MyException + { + boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + + try + { + byte[] header; + byte[] groupBytes; + byte[] filenameBytes; + byte[] meta_buff; + byte[] bs; + int groupLen; + byte[] sizeBytes; + ProtoCommon.RecvPackageInfo pkgInfo; + + if (meta_list == null) + { + meta_buff = new byte[0]; + } + else + { + meta_buff = ProtoCommon.pack_metadata(meta_list).getBytes(ClientGlobal.g_charset); + } + + filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); + sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; + Arrays.fill(sizeBytes, (byte)0); + + bs = ProtoCommon.long2buff(filenameBytes.length); + System.arraycopy(bs, 0, sizeBytes, 0, bs.length); + bs = ProtoCommon.long2buff(meta_buff.length); + System.arraycopy(bs, 0, sizeBytes, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, bs.length); + + groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + bs = group_name.getBytes(ClientGlobal.g_charset); + + Arrays.fill(groupBytes, (byte)0); + if (bs.length <= groupBytes.length) + { + groupLen = bs.length; + } + else + { + groupLen = groupBytes.length; + } + System.arraycopy(bs, 0, groupBytes, 0, groupLen); + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_SET_METADATA, + 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + 1 + groupBytes.length + + filenameBytes.length + meta_buff.length, (byte)0); + OutputStream out = storageSocket.getOutputStream(); + byte[] wholePkg = new byte[header.length + sizeBytes.length + 1 + groupBytes.length + filenameBytes.length]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length); + wholePkg[header.length+sizeBytes.length] = op_flag; + System.arraycopy(groupBytes, 0, wholePkg, header.length+sizeBytes.length+1, groupBytes.length); + System.arraycopy(filenameBytes, 0, wholePkg, header.length+sizeBytes.length+1+groupBytes.length, filenameBytes.length); + out.write(wholePkg); + if (meta_buff.length > 0) + { + out.write(meta_buff); + } + + pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + + this.errno = pkgInfo.errno; + return pkgInfo.errno; + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * get file info decoded from the filename, fetch from the storage if necessary + * @param group_name the group name + * @param remote_filename the filename + * @return FileInfo object for success, return null for fail + */ + public FileInfo get_file_info(String group_name, String remote_filename) throws IOException, MyException + { + if (remote_filename.length() < ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH + + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } + + byte[] buff = base64.decodeAuto(remote_filename.substring(ProtoCommon.FDFS_FILE_PATH_LEN, + ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH)); + + long file_size = ProtoCommon.buff2long(buff, 4 * 2); + if (((remote_filename.length() > ProtoCommon.TRUNK_LOGIC_FILENAME_LENGTH) || + ((remote_filename.length() > ProtoCommon.NORMAL_LOGIC_FILENAME_LENGTH) && ((file_size & ProtoCommon.TRUNK_FILE_MARK_SIZE) == 0))) || + ((file_size & ProtoCommon.APPENDER_FILE_SIZE) != 0)) + { //slave file or appender file + FileInfo fi = this.query_file_info(group_name, remote_filename); + if (fi == null) + { + return null; + } + return fi; + } + + FileInfo fileInfo = new FileInfo(file_size, 0, 0, ProtoCommon.getIpAddress(buff, 0)); + fileInfo.setCreateTimestamp(ProtoCommon.buff2int(buff, 4)); + if ((file_size >> 63) != 0) + { + file_size &= 0xFFFFFFFFL; //low 32 bits is file size + fileInfo.setFileSize(file_size); + } + fileInfo.setCrc32(ProtoCommon.buff2int(buff, 4 * 4)); + + return fileInfo; + } + + /** + * get file info from storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return FileInfo object for success, return null for fail + */ + public FileInfo query_file_info(String group_name, String remote_filename) throws IOException, MyException + { + boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename); + Socket storageSocket = this.storageServer.getSocket(); + + try + { + byte[] header; + byte[] groupBytes; + byte[] filenameBytes; + byte[] bs; + int groupLen; + ProtoCommon.RecvPackageInfo pkgInfo; + + filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); + groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + bs = group_name.getBytes(ClientGlobal.g_charset); + + Arrays.fill(groupBytes, (byte)0); + if (bs.length <= groupBytes.length) + { + groupLen = bs.length; + } + else + { + groupLen = groupBytes.length; + } + System.arraycopy(bs, 0, groupBytes, 0, groupLen); + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_QUERY_FILE_INFO, + + groupBytes.length + filenameBytes.length, (byte)0); + OutputStream out = storageSocket.getOutputStream(); + byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length); + System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length); + out.write(wholePkg); + + pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, + 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + + ProtoCommon.FDFS_IPADDR_SIZE); + + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + long file_size = ProtoCommon.buff2long(pkgInfo.body, 0); + int create_timestamp = (int)ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + int crc32 = (int)ProtoCommon.buff2long(pkgInfo.body, 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + String source_ip_addr = (new String(pkgInfo.body, 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, ProtoCommon.FDFS_IPADDR_SIZE)).trim(); + return new FileInfo(file_size, create_timestamp, crc32, source_ip_addr); + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + this.storageServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + finally + { + this.storageServer = null; + } + } + } + } + + /** + * check storage socket, if null create a new connection + * @param group_name the group name to upload file to, can be empty + * @return true if create a new connection + */ + protected boolean newWritableStorageConnection(String group_name) throws IOException, MyException + { + if (this.storageServer != null) + { + return false; + } + else + { + TrackerClient tracker = new TrackerClient(); + this.storageServer = tracker.getStoreStorage(this.trackerServer, group_name); + if (this.storageServer == null) + { + throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode()); + } + return true; + } + } + + /** + * check storage socket, if null create a new connection + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return true if create a new connection + */ + protected boolean newReadableStorageConnection(String group_name, String remote_filename) throws IOException, MyException + { + if (this.storageServer != null) + { + return false; + } + else + { + TrackerClient tracker = new TrackerClient(); + this.storageServer = tracker.getFetchStorage(this.trackerServer, group_name, remote_filename); + if (this.storageServer == null) + { + throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode()); + } + return true; + } + } + + /** + * check storage socket, if null create a new connection + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return true if create a new connection + */ + protected boolean newUpdatableStorageConnection(String group_name, String remote_filename) throws IOException, MyException + { + if (this.storageServer != null) + { + return false; + } + else + { + TrackerClient tracker = new TrackerClient(); + this.storageServer = tracker.getUpdateStorage(this.trackerServer, group_name, remote_filename); + if (this.storageServer == null) + { + throw new MyException("getStoreStorage fail, errno code: " + tracker.getErrorCode()); + } + return true; + } + } + + /** + * send package to storage server + * @param cmd which command to send + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + */ + protected void send_package(byte cmd, String group_name, String remote_filename) throws IOException + { + byte[] header; + byte[] groupBytes; + byte[] filenameBytes; + byte[] bs; + int groupLen; + + groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + bs = group_name.getBytes(ClientGlobal.g_charset); + filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); + + Arrays.fill(groupBytes, (byte)0); + if (bs.length <= groupBytes.length) + { + groupLen = bs.length; + } + else + { + groupLen = groupBytes.length; + } + System.arraycopy(bs, 0, groupBytes, 0, groupLen); + + header = ProtoCommon.packHeader(cmd, groupBytes.length + filenameBytes.length, (byte)0); + byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length); + System.arraycopy(filenameBytes, 0, wholePkg, header.length+groupBytes.length, filenameBytes.length); + this.storageServer.getSocket().getOutputStream().write(wholePkg); + } + + /** + * send package to storage server + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param file_offset the start offset of the file + * @param download_bytes download bytes + */ + protected void send_download_package(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException + { + byte[] header; + byte[] bsOffset; + byte[] bsDownBytes; + byte[] groupBytes; + byte[] filenameBytes; + byte[] bs; + int groupLen; + + bsOffset = ProtoCommon.long2buff(file_offset); + bsDownBytes = ProtoCommon.long2buff(download_bytes); + groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + bs = group_name.getBytes(ClientGlobal.g_charset); + filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset); + + Arrays.fill(groupBytes, (byte)0); + if (bs.length <= groupBytes.length) + { + groupLen = bs.length; + } + else + { + groupLen = groupBytes.length; + } + System.arraycopy(bs, 0, groupBytes, 0, groupLen); + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_DOWNLOAD_FILE, + bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length, (byte)0); + byte[] wholePkg = new byte[header.length + bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(bsOffset, 0, wholePkg, header.length, bsOffset.length); + System.arraycopy(bsDownBytes, 0, wholePkg, header.length+bsOffset.length, bsDownBytes.length); + System.arraycopy(groupBytes, 0, wholePkg, header.length+bsOffset.length+bsDownBytes.length, groupBytes.length); + System.arraycopy(filenameBytes, 0, wholePkg, header.length+bsOffset.length+bsDownBytes.length+groupBytes.length, filenameBytes.length); + this.storageServer.getSocket().getOutputStream().write(wholePkg); + } +} diff --git a/src/org/csource/fastdfs/StorageClient1.java b/src/org/csource/fastdfs/StorageClient1.java new file mode 100644 index 0000000..c6b7f47 --- /dev/null +++ b/src/org/csource/fastdfs/StorageClient1.java @@ -0,0 +1,799 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +import java.io.*; +import java.util.*; +import java.net.*; +import org.csource.common.*; + +/** +* Storage client for 1 field file id: combined group name and filename +* @author Happy Fish / YuQing +* @version Version 1.21 +*/ +public class StorageClient1 extends StorageClient +{ + public static final String SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR = "/"; + +/** +* constructor +*/ + public StorageClient1() + { + super(); + } + +/** +* constructor +* @param trackerServer the tracker server, can be null +* @param storageServer the storage server, can be null +*/ + public StorageClient1(TrackerServer trackerServer, StorageServer storageServer) + { + super(trackerServer, storageServer); + } + + public static byte split_file_id(String file_id, String[] results) + { + int pos = file_id.indexOf(SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR); + if ((pos <= 0) || (pos == file_id.length() - 1)) + { + return ProtoCommon.ERR_NO_EINVAL; + } + + results[0] = file_id.substring(0, pos); //group name + results[1] = file_id.substring(pos + 1); //file name + return 0; + } + + /** + * upload file to storage server (by file name) + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_file(local_filename, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by file name) + * @param group_name the group name to upload file to, can be empty + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String group_name, String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_file(group_name, local_filename, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by file buff) + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(byte[] file_buff, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_file(file_buff, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by file buff) + * @param group_name the group name to upload file to, can be empty + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String group_name, byte[] file_buff, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_file(group_name, file_buff, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by callback) + * @param group_name the group name to upload file to, can be empty + * @param file_size the file size + * @param callback the write data callback object + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String group_name, long file_size, + UploadCallback callback, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_file(group_name, file_size, callback, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload appender file to storage server (by file name) + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_appender_file1(String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_appender_file(local_filename, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload appender file to storage server (by file name) + * @param group_name the group name to upload file to, can be empty + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_appender_file1(String group_name, String local_filename, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload appender file to storage server (by file buff) + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_appender_file1(byte[] file_buff, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_appender_file(file_buff, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload appender file to storage server (by file buff) + * @param group_name the group name to upload file to, can be empty + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_appender_file1(String group_name, byte[] file_buff, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_appender_file(group_name, file_buff, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload appender file to storage server (by callback) + * @param group_name the group name to upload file to, can be empty + * @param file_size the file size + * @param callback the write data callback object + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_appender_file1(String group_name, long file_size, + UploadCallback callback, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String parts[] = this.upload_appender_file(group_name, file_size, callback, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by file name, slave file mode) + * @param master_file_id the master file id to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param local_filename local filename to upload + * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String master_file_id, String prefix_name, + String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(master_file_id, parts); + if (this.errno != 0) + { + return null; + } + + parts = this.upload_file(parts[0], parts[1], prefix_name, + local_filename, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by file buff, slave file mode) + * @param master_file_id the master file id to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String master_file_id, String prefix_name, + byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(master_file_id, parts); + if (this.errno != 0) + { + return null; + } + + parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by file buff, slave file mode) + * @param master_file_id the master file id to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_buff file content/buff + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String master_file_id, String prefix_name, + byte[] file_buff, int offset, int length, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(master_file_id, parts); + if (this.errno != 0) + { + return null; + } + + parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff, + offset, length, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * upload file to storage server (by callback) + * @param master_file_id the master file id to generate the slave file + * @param prefix_name the prefix name to generate the slave file + * @param file_size the file size + * @param callback the write data callback object + * @param file_ext_name file ext name, do not include dot(.) + * @param meta_list meta info array + * @return file id(including group name and filename) if success,
+ * return null if fail + */ + public String upload_file1(String master_file_id, String prefix_name, long file_size, + UploadCallback callback, String file_ext_name, + NameValuePair[] meta_list) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(master_file_id, parts); + if (this.errno != 0) + { + return null; + } + + parts = this.upload_file(parts[0], parts[1], prefix_name, file_size, callback, file_ext_name, meta_list); + if (parts != null) + { + return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1]; + } + else + { + return null; + } + } + + /** + * append file to storage server (by file name) + * @param appender_file_id the appender file id + * @param local_filename local filename to append + * @return 0 for success, != 0 for error (error no) + */ + public int append_file1(String appender_file_id, String local_filename) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.append_file(parts[0], parts[1], local_filename); + } + + /** + * append file to storage server (by file buff) + * @param appender_file_id the appender file id + * @param file_buff file content/buff + * @return 0 for success, != 0 for error (error no) + */ + public int append_file1(String appender_file_id, byte[] file_buff) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.append_file(parts[0], parts[1], file_buff); + } + + /** + * append file to storage server (by file buff) + * @param appender_file_id the appender file id + * @param file_buff file content/buffer + * @param offset start offset of the buffer + * @param length the length of the buffer to append + * @return 0 for success, != 0 for error (error no) + */ + public int append_file1(String appender_file_id, byte[] file_buff, int offset, int length) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.append_file(parts[0], parts[1], file_buff, offset, length); + } + + /** + * append file to storage server (by callback) + * @param appender_file_id the appender file id + * @param file_size the file size + * @param callback the write data callback object + * @return 0 for success, != 0 for error (error no) + */ + public int append_file1(String appender_file_id, long file_size, UploadCallback callback) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.append_file(parts[0], parts[1], file_size, callback); + } + + /** + * modify appender file to storage server (by file name) + * @param appender_file_id the appender file id + * @param file_offset the offset of appender file + * @param local_filename local filename to append + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file1(String appender_file_id, + long file_offset, String local_filename) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.modify_file(parts[0], parts[1], file_offset, local_filename); + } + + /** + * modify appender file to storage server (by file buff) + * @param appender_file_id the appender file id + * @param file_offset the offset of appender file + * @param file_buff file content/buff + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file1(String appender_file_id, + long file_offset, byte[] file_buff) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.modify_file(parts[0], parts[1], file_offset, file_buff); + } + + /** + * modify appender file to storage server (by file buff) + * @param appender_file_id the appender file id + * @param file_offset the offset of appender file + * @param file_buff file content/buff + * @param buffer_offset start offset of the buff + * @param buffer_length the length of buff to modify + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file1(String appender_file_id, + long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.modify_file(parts[0], parts[1], file_offset, + file_buff, buffer_offset, buffer_length); + } + + /** + * modify appender file to storage server (by callback) + * @param appender_file_id the appender file id + * @param file_offset the offset of appender file + * @param modify_size the modify size + * @param callback the write data callback object + * @return 0 for success, != 0 for error (error no) + */ + public int modify_file1(String appender_file_id, + long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.modify_file(parts[0], parts[1], file_offset, modify_size, callback); + } + + /** + * delete file from storage server + * @param file_id the file id(including group name and filename) + * @return 0 for success, none zero for fail (error code) + */ + public int delete_file1(String file_id) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.delete_file(parts[0], parts[1]); + } + + /** + * truncate appender file to size 0 from storage server + * @param appender_file_id the appender file id + * @return 0 for success, none zero for fail (error code) + */ + public int truncate_file1(String appender_file_id) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.truncate_file(parts[0], parts[1]); + } + + /** + * truncate appender file from storage server + * @param appender_file_id the appender file id + * @param truncated_file_size truncated file size + * @return 0 for success, none zero for fail (error code) + */ + public int truncate_file1(String appender_file_id, long truncated_file_size) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.truncate_file(parts[0], parts[1], truncated_file_size); + } + + /** + * download file from storage server + * @param file_id the file id(including group name and filename) + * @return file content/buffer, return null if fail + */ + public byte[] download_file1(String file_id) throws IOException, MyException + { + final long file_offset = 0; + final long download_bytes = 0; + + return this.download_file1(file_id, file_offset, download_bytes); + } + + /** + * download file from storage server + * @param file_id the file id(including group name and filename) + * @param file_offset the start offset of the file + * @param download_bytes download bytes, 0 for remain bytes from offset + * @return file content/buff, return null if fail + */ + public byte[] download_file1(String file_id, long file_offset, long download_bytes) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return null; + } + + return this.download_file(parts[0], parts[1], file_offset, download_bytes); + } + + /** + * download file from storage server + * @param file_id the file id(including group name and filename) + * @param local_filename the filename on local + * @return 0 success, return none zero errno if fail + */ + public int download_file1(String file_id, String local_filename) throws IOException, MyException + { + final long file_offset = 0; + final long download_bytes = 0; + + return this.download_file1(file_id, file_offset, download_bytes, local_filename); + } + + /** + * download file from storage server + * @param file_id the file id(including group name and filename) + * @param file_offset the start offset of the file + * @param download_bytes download bytes, 0 for remain bytes from offset + * @param local_filename the filename on local + * @return 0 success, return none zero errno if fail + */ + public int download_file1(String file_id, long file_offset, long download_bytes, String local_filename) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.download_file(parts[0], parts[1], file_offset, download_bytes, local_filename); + } + + /** + * download file from storage server + * @param file_id the file id(including group name and filename) + * @param callback the callback object, will call callback.recv() when data arrive + * @return 0 success, return none zero errno if fail + */ + public int download_file1(String file_id, DownloadCallback callback) throws IOException, MyException + { + final long file_offset = 0; + final long download_bytes = 0; + + return this.download_file1(file_id, file_offset, download_bytes, callback); + } + + /** + * download file from storage server + * @param file_id the file id(including group name and filename) + * @param file_offset the start offset of the file + * @param download_bytes download bytes, 0 for remain bytes from offset + * @param callback the callback object, will call callback.recv() when data arrive + * @return 0 success, return none zero errno if fail + */ + public int download_file1(String file_id, long file_offset, long download_bytes, DownloadCallback callback) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.download_file(parts[0], parts[1], file_offset, download_bytes, callback); + } + + /** + * get all metadata items from storage server + * @param file_id the file id(including group name and filename) + * @return meta info array, return null if fail + */ + public NameValuePair[] get_metadata1(String file_id)throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return null; + } + + return this.get_metadata(parts[0], parts[1]); + } + + /** + * set metadata items to storage server + * @param file_id the file id(including group name and filename) + * @param meta_list meta item array + * @param op_flag flag, can be one of following values:
+ *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old + * metadata items
+ *
  • ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when + * the metadata item not exist, otherwise update it
+ * @return 0 for success, !=0 fail (error code) + */ + public int set_metadata1(String file_id, NameValuePair[] meta_list, byte op_flag) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return this.errno; + } + + return this.set_metadata(parts[0], parts[1], meta_list, op_flag); + } + + /** + * get file info from storage server + * @param file_id the file id(including group name and filename) + * @return FileInfo object for success, return null for fail + */ + public FileInfo query_file_info1(String file_id) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return null; + } + + return this.query_file_info(parts[0], parts[1]); + } + + /** + * get file info decoded from filename + * @param file_id the file id(including group name and filename) + * @return FileInfo object for success, return null for fail + */ + public FileInfo get_file_info1(String file_id) throws IOException, MyException + { + String[] parts = new String[2]; + this.errno = this.split_file_id(file_id, parts); + if (this.errno != 0) + { + return null; + } + + return this.get_file_info(parts[0], parts[1]); + } +} diff --git a/src/org/csource/fastdfs/StorageServer.java b/src/org/csource/fastdfs/StorageServer.java new file mode 100644 index 0000000..c4f3daf --- /dev/null +++ b/src/org/csource/fastdfs/StorageServer.java @@ -0,0 +1,63 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +import java.io.*; +import java.util.*; +import java.net.*; +import org.csource.common.*; + +/** +* Storage Server Info +* @author Happy Fish / YuQing +* @version Version 1.11 +*/ +public class StorageServer extends TrackerServer +{ + protected int store_path_index = 0; + +/** +* Constructor +* @param ip_addr the ip address of storage server +* @param port the port of storage server +* @param store_path the store path index on the storage server +*/ + public StorageServer(String ip_addr, int port, int store_path) throws IOException + { + super(ClientGlobal.getSocket(ip_addr, port), new InetSocketAddress(ip_addr, port)); + this.store_path_index = store_path; + } + +/** +* Constructor +* @param ip_addr the ip address of storage server +* @param port the port of storage server +* @param store_path the store path index on the storage server +*/ + public StorageServer(String ip_addr, int port, byte store_path) throws IOException + { + super(ClientGlobal.getSocket(ip_addr, port), new InetSocketAddress(ip_addr, port)); + if (store_path < 0) + { + this.store_path_index = 256 + store_path; + } + else + { + this.store_path_index = store_path; + } + } + +/** +* @return the store path index on the storage server +*/ + public int getStorePathIndex() + { + return this.store_path_index; + } +} diff --git a/src/org/csource/fastdfs/StructBase.java b/src/org/csource/fastdfs/StructBase.java new file mode 100644 index 0000000..1f7ab90 --- /dev/null +++ b/src/org/csource/fastdfs/StructBase.java @@ -0,0 +1,79 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +import java.io.UnsupportedEncodingException; +import java.util.Date; + +/** +* C struct body decoder +* @author Happy Fish / YuQing +* @version Version 1.17 +*/ +public abstract class StructBase +{ + protected static class FieldInfo + { + protected String name; + protected int offset; + protected int size; + + public FieldInfo(String name, int offset, int size) + { + this.name = name; + this.offset = offset; + this.size = size; + } + } + +/** +* set fields +* @param bs byte array +* @param offset start offset +*/ + public abstract void setFields(byte[] bs, int offset); + + protected String stringValue(byte[] bs, int offset, FieldInfo filedInfo) + { + try + { + return (new String(bs, offset + filedInfo.offset, filedInfo.size, ClientGlobal.g_charset)).trim(); + } + catch(UnsupportedEncodingException ex) + { + ex.printStackTrace(); + return null; + } + } + + protected long longValue(byte[] bs, int offset, FieldInfo filedInfo) + { + return ProtoCommon.buff2long(bs, offset + filedInfo.offset); + } + + protected int intValue(byte[] bs, int offset, FieldInfo filedInfo) + { + return (int)ProtoCommon.buff2long(bs, offset + filedInfo.offset); + } + + protected byte byteValue(byte[] bs, int offset, FieldInfo filedInfo) + { + return bs[offset + filedInfo.offset]; + } + + protected boolean booleanValue(byte[] bs, int offset, FieldInfo filedInfo) + { + return bs[offset + filedInfo.offset] != 0; + } + + protected Date dateValue(byte[] bs, int offset, FieldInfo filedInfo) + { + return new Date(ProtoCommon.buff2long(bs, offset + filedInfo.offset) * 1000); + } +} diff --git a/src/org/csource/fastdfs/StructGroupStat.java b/src/org/csource/fastdfs/StructGroupStat.java new file mode 100644 index 0000000..41cbb47 --- /dev/null +++ b/src/org/csource/fastdfs/StructGroupStat.java @@ -0,0 +1,226 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +/** +* C struct body decoder +* @author Happy Fish / YuQing +* @version Version 1.18 +*/ +public class StructGroupStat extends StructBase +{ + protected static final int FIELD_INDEX_GROUP_NAME = 0; + protected static final int FIELD_INDEX_TOTAL_MB = 1; + protected static final int FIELD_INDEX_FREE_MB = 2; + protected static final int FIELD_INDEX_TRUNK_FREE_MB = 3; + protected static final int FIELD_INDEX_STORAGE_COUNT = 4; + protected static final int FIELD_INDEX_STORAGE_PORT = 5; + protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 6; + protected static final int FIELD_INDEX_ACTIVE_COUNT = 7; + protected static final int FIELD_INDEX_CURRENT_WRITE_SERVER = 8; + protected static final int FIELD_INDEX_STORE_PATH_COUNT = 9; + protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 10; + protected static final int FIELD_INDEX_CURRENT_TRUNK_FILE_ID = 11; + + protected static int fieldsTotalSize; + protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[12]; + + static + { + int offset = 0; + fieldsArray[FIELD_INDEX_GROUP_NAME] = new StructBase.FieldInfo("groupName", offset, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1); + offset += ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1; + + fieldsArray[FIELD_INDEX_TOTAL_MB] = new StructBase.FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_FREE_MB] = new StructBase.FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TRUNK_FREE_MB] = new StructBase.FieldInfo("trunkFreeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORAGE_COUNT] = new StructBase.FieldInfo("storageCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORAGE_PORT] = new StructBase.FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new StructBase.FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_ACTIVE_COUNT] = new StructBase.FieldInfo("activeCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER] = new StructBase.FieldInfo("currentWriteServer", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new StructBase.FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new StructBase.FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID] = new StructBase.FieldInfo("currentTrunkFileId", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsTotalSize = offset; + } + + protected String groupName; //name of this group + protected long totalMB; //total disk storage in MB + protected long freeMB; //free disk space in MB + protected long trunkFreeMB; //trunk free space in MB + protected int storageCount; //storage server count + protected int storagePort; //storage server port + protected int storageHttpPort; //storage server HTTP port + protected int activeCount; //active storage server count + protected int currentWriteServer; //current storage server index to upload file + protected int storePathCount; //store base path count of each storage server + protected int subdirCountPerPath; //sub dir count per store path + protected int currentTrunkFileId; //current trunk file id + +/** +* get group name +* @return group name +*/ + public String getGroupName() + { + return this.groupName; + } + +/** +* get total disk space in MB +* @return total disk space in MB +*/ + public long getTotalMB() + { + return this.totalMB; + } + +/** +* get free disk space in MB +* @return free disk space in MB +*/ + public long getFreeMB() + { + return this.freeMB; + } + +/** +* get trunk free space in MB +* @return trunk free space in MB +*/ + public long getTrunkFreeMB() + { + return this.trunkFreeMB; + } + +/** +* get storage server count in this group +* @return storage server count in this group +*/ + public int getStorageCount() + { + return this.storageCount; + } + +/** +* get active storage server count in this group +* @return active storage server count in this group +*/ + public int getActiveCount() + { + return this.activeCount; + } + +/** +* get storage server port +* @return storage server port +*/ + public int getStoragePort() + { + return this.storagePort; + } + +/** +* get storage server HTTP port +* @return storage server HTTP port +*/ + public int getStorageHttpPort() + { + return this.storageHttpPort; + } + +/** +* get current storage server index to upload file +* @return current storage server index to upload file +*/ + public int getCurrentWriteServer() + { + return this.currentWriteServer; + } + +/** +* get store base path count of each storage server +* @return store base path count of each storage server +*/ + public int getStorePathCount() + { + return this.storePathCount; + } + +/** +* get sub dir count per store path +* @return sub dir count per store path +*/ + public int getSubdirCountPerPath() + { + return this.subdirCountPerPath; + } + +/** +* get current trunk file id +* @return current trunk file id +*/ + public int getCurrentTrunkFileId() + { + return this.currentTrunkFileId; + } + +/** +* set fields +* @param bs byte array +* @param offset start offset +*/ + public void setFields(byte[] bs, int offset) + { + this.groupName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_GROUP_NAME]); + this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]); + this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]); + this.trunkFreeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TRUNK_FREE_MB]); + this.storageCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_COUNT]); + this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]); + this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]); + this.activeCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_ACTIVE_COUNT]); + this.currentWriteServer = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER]); + this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]); + this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]); + this.currentTrunkFileId = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID]); + } + +/** +* get fields total size +* @return fields total size +*/ + public static int getFieldsTotalSize() + { + return fieldsTotalSize; + } +} diff --git a/src/org/csource/fastdfs/StructStorageStat.java b/src/org/csource/fastdfs/StructStorageStat.java new file mode 100644 index 0000000..26975dd --- /dev/null +++ b/src/org/csource/fastdfs/StructStorageStat.java @@ -0,0 +1,956 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +import java.util.Date; + +/** +* C struct body decoder +* @author Happy Fish / YuQing +* @version Version 1.25 +*/ +public class StructStorageStat extends StructBase +{ + protected static final int FIELD_INDEX_STATUS = 0; + protected static final int FIELD_INDEX_ID = 1; + protected static final int FIELD_INDEX_IP_ADDR = 2; + protected static final int FIELD_INDEX_DOMAIN_NAME = 3; + protected static final int FIELD_INDEX_SRC_IP_ADDR = 4; + protected static final int FIELD_INDEX_VERSION = 5; + protected static final int FIELD_INDEX_JOIN_TIME = 6; + protected static final int FIELD_INDEX_UP_TIME = 7; + protected static final int FIELD_INDEX_TOTAL_MB = 8; + protected static final int FIELD_INDEX_FREE_MB = 9; + protected static final int FIELD_INDEX_UPLOAD_PRIORITY = 10; + protected static final int FIELD_INDEX_STORE_PATH_COUNT = 11; + protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 12; + protected static final int FIELD_INDEX_CURRENT_WRITE_PATH = 13; + protected static final int FIELD_INDEX_STORAGE_PORT = 14; + protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 15; + + protected static final int FIELD_INDEX_CONNECTION_ALLOC_COUNT = 16; + protected static final int FIELD_INDEX_CONNECTION_CURRENT_COUNT = 17; + protected static final int FIELD_INDEX_CONNECTION_MAX_COUNT = 18; + + protected static final int FIELD_INDEX_TOTAL_UPLOAD_COUNT = 19; + protected static final int FIELD_INDEX_SUCCESS_UPLOAD_COUNT = 20; + protected static final int FIELD_INDEX_TOTAL_APPEND_COUNT = 21; + protected static final int FIELD_INDEX_SUCCESS_APPEND_COUNT = 22; + protected static final int FIELD_INDEX_TOTAL_MODIFY_COUNT = 23; + protected static final int FIELD_INDEX_SUCCESS_MODIFY_COUNT = 24; + protected static final int FIELD_INDEX_TOTAL_TRUNCATE_COUNT = 25; + protected static final int FIELD_INDEX_SUCCESS_TRUNCATE_COUNT = 26; + protected static final int FIELD_INDEX_TOTAL_SET_META_COUNT = 27; + protected static final int FIELD_INDEX_SUCCESS_SET_META_COUNT = 28; + protected static final int FIELD_INDEX_TOTAL_DELETE_COUNT = 29; + protected static final int FIELD_INDEX_SUCCESS_DELETE_COUNT = 30; + protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_COUNT = 31; + protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT = 32; + protected static final int FIELD_INDEX_TOTAL_GET_META_COUNT = 33; + protected static final int FIELD_INDEX_SUCCESS_GET_META_COUNT = 34; + protected static final int FIELD_INDEX_TOTAL_CREATE_LINK_COUNT = 35; + protected static final int FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT= 36; + protected static final int FIELD_INDEX_TOTAL_DELETE_LINK_COUNT = 37; + protected static final int FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT= 38; + protected static final int FIELD_INDEX_TOTAL_UPLOAD_BYTES = 39; + protected static final int FIELD_INDEX_SUCCESS_UPLOAD_BYTES = 40; + protected static final int FIELD_INDEX_TOTAL_APPEND_BYTES = 41; + protected static final int FIELD_INDEX_SUCCESS_APPEND_BYTES = 42; + protected static final int FIELD_INDEX_TOTAL_MODIFY_BYTES = 43; + protected static final int FIELD_INDEX_SUCCESS_MODIFY_BYTES = 44; + protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_BYTES = 45; + protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES = 46; + protected static final int FIELD_INDEX_TOTAL_SYNC_IN_BYTES = 47; + protected static final int FIELD_INDEX_SUCCESS_SYNC_IN_BYTES = 48; + protected static final int FIELD_INDEX_TOTAL_SYNC_OUT_BYTES = 49; + protected static final int FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES = 50; + protected static final int FIELD_INDEX_TOTAL_FILE_OPEN_COUNT = 51; + protected static final int FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT = 52; + protected static final int FIELD_INDEX_TOTAL_FILE_READ_COUNT = 53; + protected static final int FIELD_INDEX_SUCCESS_FILE_READ_COUNT = 54; + protected static final int FIELD_INDEX_TOTAL_FILE_WRITE_COUNT = 55; + protected static final int FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT = 56; + protected static final int FIELD_INDEX_LAST_SOURCE_UPDATE = 57; + protected static final int FIELD_INDEX_LAST_SYNC_UPDATE = 58; + protected static final int FIELD_INDEX_LAST_SYNCED_TIMESTAMP = 59; + protected static final int FIELD_INDEX_LAST_HEART_BEAT_TIME = 60; + protected static final int FIELD_INDEX_IF_TRUNK_FILE = 61; + + protected static int fieldsTotalSize; + protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[62]; + + static + { + int offset = 0; + + fieldsArray[FIELD_INDEX_STATUS] = new StructBase.FieldInfo("status", offset, 1); + offset += 1; + + fieldsArray[FIELD_INDEX_ID] = new StructBase.FieldInfo("id", offset, ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE); + offset += ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE; + + fieldsArray[FIELD_INDEX_IP_ADDR] = new StructBase.FieldInfo("ipAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE); + offset += ProtoCommon.FDFS_IPADDR_SIZE; + + fieldsArray[FIELD_INDEX_DOMAIN_NAME] = new StructBase.FieldInfo("domainName", offset, ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE); + offset += ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE; + + fieldsArray[FIELD_INDEX_SRC_IP_ADDR] = new StructBase.FieldInfo("srcIpAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE); + offset += ProtoCommon.FDFS_IPADDR_SIZE; + + fieldsArray[FIELD_INDEX_VERSION] = new StructBase.FieldInfo("version", offset, ProtoCommon.FDFS_VERSION_SIZE); + offset += ProtoCommon.FDFS_VERSION_SIZE; + + fieldsArray[FIELD_INDEX_JOIN_TIME] = new StructBase.FieldInfo("joinTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_UP_TIME] = new StructBase.FieldInfo("upTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_MB] = new StructBase.FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_FREE_MB] = new StructBase.FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY] = new StructBase.FieldInfo("uploadPriority", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new StructBase.FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new StructBase.FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH] = new StructBase.FieldInfo("currentWritePath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORAGE_PORT] = new StructBase.FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new StructBase.FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT] = new StructBase.FieldInfo("connectionAllocCount", offset, 4); + offset += 4; + + fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT] = new StructBase.FieldInfo("connectionCurrentCount", offset, 4); + offset += 4; + + fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT] = new StructBase.FieldInfo("connectionMaxCount", offset, 4); + offset += 4; + + fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT] = new StructBase.FieldInfo("totalUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT] = new StructBase.FieldInfo("successUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT] = new StructBase.FieldInfo("totalAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT] = new StructBase.FieldInfo("successAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT] = new StructBase.FieldInfo("totalModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT] = new StructBase.FieldInfo("successModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT] = new StructBase.FieldInfo("totalTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT] = new StructBase.FieldInfo("successTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT] = new StructBase.FieldInfo("totalSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT] = new StructBase.FieldInfo("successSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT] = new StructBase.FieldInfo("totalDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT] = new StructBase.FieldInfo("successDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT] = new StructBase.FieldInfo("totalDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT] = new StructBase.FieldInfo("successDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT] = new StructBase.FieldInfo("totalGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT] = new StructBase.FieldInfo("successGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT] = new StructBase.FieldInfo("totalCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT] = new StructBase.FieldInfo("successCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT] = new StructBase.FieldInfo("totalDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT] = new StructBase.FieldInfo("successDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES] = new StructBase.FieldInfo("totalUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES] = new StructBase.FieldInfo("successUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES] = new StructBase.FieldInfo("totalAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES] = new StructBase.FieldInfo("successAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES] = new StructBase.FieldInfo("totalModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES] = new StructBase.FieldInfo("successModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES] = new StructBase.FieldInfo("totalDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES] = new StructBase.FieldInfo("successDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES] = new StructBase.FieldInfo("totalSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES] = new StructBase.FieldInfo("successSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES] = new StructBase.FieldInfo("totalSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES] = new StructBase.FieldInfo("successSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT] = new StructBase.FieldInfo("totalFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT] = new StructBase.FieldInfo("successFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT] = new StructBase.FieldInfo("totalFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT] = new StructBase.FieldInfo("successFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT] = new StructBase.FieldInfo("totalFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT] = new StructBase.FieldInfo("successFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE] = new StructBase.FieldInfo("lastSourceUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE] = new StructBase.FieldInfo("lastSyncUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP] = new StructBase.FieldInfo("lastSyncedTimestamp", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME] = new StructBase.FieldInfo("lastHeartBeatTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); + offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + fieldsArray[FIELD_INDEX_IF_TRUNK_FILE] = new StructBase.FieldInfo("ifTrunkServer", offset, 1); + offset += 1; + + fieldsTotalSize = offset; + } + + protected byte status; + protected String id; + protected String ipAddr; + protected String srcIpAddr; + protected String domainName; //http domain name + protected String version; + protected long totalMB; //total disk storage in MB + protected long freeMB; //free disk storage in MB + protected int uploadPriority; //upload priority + protected Date joinTime; //storage join timestamp (create timestamp) + protected Date upTime; //storage service started timestamp + protected int storePathCount; //store base path count of each storage server + protected int subdirCountPerPath; + protected int storagePort; + protected int storageHttpPort; //storage http server port + protected int currentWritePath; //current write path index + protected int connectionAllocCount; + protected int connectionCurrentCount; + protected int connectionMaxCount; + protected long totalUploadCount; + protected long successUploadCount; + protected long totalAppendCount; + protected long successAppendCount; + protected long totalModifyCount; + protected long successModifyCount; + protected long totalTruncateCount; + protected long successTruncateCount; + protected long totalSetMetaCount; + protected long successSetMetaCount; + protected long totalDeleteCount; + protected long successDeleteCount; + protected long totalDownloadCount; + protected long successDownloadCount; + protected long totalGetMetaCount; + protected long successGetMetaCount; + protected long totalCreateLinkCount; + protected long successCreateLinkCount; + protected long totalDeleteLinkCount; + protected long successDeleteLinkCount; + protected long totalUploadBytes; + protected long successUploadBytes; + protected long totalAppendBytes; + protected long successAppendBytes; + protected long totalModifyBytes; + protected long successModifyBytes; + protected long totalDownloadloadBytes; + protected long successDownloadloadBytes; + protected long totalSyncInBytes; + protected long successSyncInBytes; + protected long totalSyncOutBytes; + protected long successSyncOutBytes; + protected long totalFileOpenCount; + protected long successFileOpenCount; + protected long totalFileReadCount; + protected long successFileReadCount; + protected long totalFileWriteCount; + protected long successFileWriteCount; + protected Date lastSourceUpdate; + protected Date lastSyncUpdate; + protected Date lastSyncedTimestamp; + protected Date lastHeartBeatTime; + protected boolean ifTrunkServer; + +/** +* get storage status +* @return storage status +*/ + public byte getStatus() + { + return this.status; + } + +/** +* get storage server id +* @return storage server id +*/ + public String getId() + { + return this.id; + } + +/** +* get storage server ip address +* @return storage server ip address +*/ + public String getIpAddr() + { + return this.ipAddr; + } + +/** +* get source storage ip address +* @return source storage ip address +*/ + public String getSrcIpAddr() + { + return this.srcIpAddr; + } + +/** +* get the domain name of the storage server +* @return the domain name of the storage server +*/ + public String getDomainName() + { + return this.domainName; + } + +/** +* get storage version +* @return storage version +*/ + public String getVersion() + { + return this.version; + } + +/** +* get total disk space in MB +* @return total disk space in MB +*/ + public long getTotalMB() + { + return this.totalMB; + } + +/** +* get free disk space in MB +* @return free disk space in MB +*/ + public long getFreeMB() + { + return this.freeMB; + } + +/** +* get storage server upload priority +* @return storage server upload priority +*/ + public int getUploadPriority() + { + return this.uploadPriority; + } + +/** +* get storage server join time +* @return storage server join time +*/ + public Date getJoinTime() + { + return this.joinTime; + } + +/** +* get storage server up time +* @return storage server up time +*/ + public Date getUpTime() + { + return this.upTime; + } + +/** +* get store base path count of each storage server +* @return store base path count of each storage server +*/ + public int getStorePathCount() + { + return this.storePathCount; + } + +/** +* get sub dir count per store path +* @return sub dir count per store path +*/ + public int getSubdirCountPerPath() + { + return this.subdirCountPerPath; + } + +/** +* get storage server port +* @return storage server port +*/ + public int getStoragePort() + { + return this.storagePort; + } + +/** +* get storage server HTTP port +* @return storage server HTTP port +*/ + public int getStorageHttpPort() + { + return this.storageHttpPort; + } + +/** +* get current write path index +* @return current write path index +*/ + public int getCurrentWritePath() + { + return this.currentWritePath; + } + +/** +* get total upload file count +* @return total upload file count +*/ + public long getTotalUploadCount() + { + return this.totalUploadCount; + } + +/** +* get success upload file count +* @return success upload file count +*/ + public long getSuccessUploadCount() + { + return this.successUploadCount; + } + +/** +* get total append count +* @return total append count +*/ + public long getTotalAppendCount() + { + return this.totalAppendCount; + } + +/** +* get success append count +* @return success append count +*/ + public long getSuccessAppendCount() + { + return this.successAppendCount; + } + +/** +* get total modify count +* @return total modify count +*/ + public long getTotalModifyCount() + { + return this.totalModifyCount; + } + +/** +* get success modify count +* @return success modify count +*/ + public long getSuccessModifyCount() + { + return this.successModifyCount; + } + +/** +* get total truncate count +* @return total truncate count +*/ + public long getTotalTruncateCount() + { + return this.totalTruncateCount; + } + +/** +* get success truncate count +* @return success truncate count +*/ + public long getSuccessTruncateCount() + { + return this.successTruncateCount; + } + +/** +* get total set meta data count +* @return total set meta data count +*/ + public long getTotalSetMetaCount() + { + return this.totalSetMetaCount; + } + +/** +* get success set meta data count +* @return success set meta data count +*/ + public long getSuccessSetMetaCount() + { + return this.successSetMetaCount; + } + +/** +* get total delete file count +* @return total delete file count +*/ + public long getTotalDeleteCount() + { + return this.totalDeleteCount; + } + +/** +* get success delete file count +* @return success delete file count +*/ + public long getSuccessDeleteCount() + { + return this.successDeleteCount; + } + +/** +* get total download file count +* @return total download file count +*/ + public long getTotalDownloadCount() + { + return this.totalDownloadCount; + } + +/** +* get success download file count +* @return success download file count +*/ + public long getSuccessDownloadCount() + { + return this.successDownloadCount; + } + +/** +* get total get metadata count +* @return total get metadata count +*/ + public long getTotalGetMetaCount() + { + return this.totalGetMetaCount; + } + +/** +* get success get metadata count +* @return success get metadata count +*/ + public long getSuccessGetMetaCount() + { + return this.successGetMetaCount; + } + +/** +* get total create linke count +* @return total create linke count +*/ + public long getTotalCreateLinkCount() + { + return this.totalCreateLinkCount; + } + +/** +* get success create linke count +* @return success create linke count +*/ + public long getSuccessCreateLinkCount() + { + return this.successCreateLinkCount; + } + +/** +* get total delete link count +* @return total delete link count +*/ + public long getTotalDeleteLinkCount() + { + return this.totalDeleteLinkCount; + } + +/** +* get success delete link count +* @return success delete link count +*/ + public long getSuccessDeleteLinkCount() + { + return this.successDeleteLinkCount; + } + +/** +* get total upload file bytes +* @return total upload file bytes +*/ + public long getTotalUploadBytes() + { + return this.totalUploadBytes; + } + +/** +* get success upload file bytes +* @return success upload file bytes +*/ + public long getSuccessUploadBytes() + { + return this.successUploadBytes; + } + +/** +* get total append bytes +* @return total append bytes +*/ + public long getTotalAppendBytes() + { + return this.totalAppendBytes; + } + +/** +* get success append bytes +* @return success append bytes +*/ + public long getSuccessAppendBytes() + { + return this.successAppendBytes; + } + +/** +* get total modify bytes +* @return total modify bytes +*/ + public long getTotalModifyBytes() + { + return this.totalModifyBytes; + } + +/** +* get success modify bytes +* @return success modify bytes +*/ + public long getSuccessModifyBytes() + { + return this.successModifyBytes; + } + +/** +* get total download file bytes +* @return total download file bytes +*/ + public long getTotalDownloadloadBytes() + { + return this.totalDownloadloadBytes; + } + +/** +* get success download file bytes +* @return success download file bytes +*/ + public long getSuccessDownloadloadBytes() + { + return this.successDownloadloadBytes; + } + +/** +* get total sync in bytes +* @return total sync in bytes +*/ + public long getTotalSyncInBytes() + { + return this.totalSyncInBytes; + } + +/** +* get success sync in bytes +* @return success sync in bytes +*/ + public long getSuccessSyncInBytes() + { + return this.successSyncInBytes; + } + +/** +* get total sync out bytes +* @return total sync out bytes +*/ + public long getTotalSyncOutBytes() + { + return this.totalSyncOutBytes; + } + +/** +* get success sync out bytes +* @return success sync out bytes +*/ + public long getSuccessSyncOutBytes() + { + return this.successSyncOutBytes; + } + +/** +* get total file opened count +* @return total file opened bytes +*/ + public long getTotalFileOpenCount() + { + return this.totalFileOpenCount; + } + +/** +* get success file opened count +* @return success file opened count +*/ + public long getSuccessFileOpenCount() + { + return this.successFileOpenCount; + } + +/** +* get total file read count +* @return total file read bytes +*/ + public long getTotalFileReadCount() + { + return this.totalFileReadCount; + } + +/** +* get success file read count +* @return success file read count +*/ + public long getSuccessFileReadCount() + { + return this.successFileReadCount; + } + +/** +* get total file write count +* @return total file write bytes +*/ + public long getTotalFileWriteCount() + { + return this.totalFileWriteCount; + } + +/** +* get success file write count +* @return success file write count +*/ + public long getSuccessFileWriteCount() + { + return this.successFileWriteCount; + } + +/** +* get last source update timestamp +* @return last source update timestamp +*/ + public Date getLastSourceUpdate() + { + return this.lastSourceUpdate; + } + +/** +* get last synced update timestamp +* @return last synced update timestamp +*/ + public Date getLastSyncUpdate() + { + return this.lastSyncUpdate; + } + +/** +* get last synced timestamp +* @return last synced timestamp +*/ + public Date getLastSyncedTimestamp() + { + return this.lastSyncedTimestamp; + } + +/** +* get last heart beat timestamp +* @return last heart beat timestamp +*/ + public Date getLastHeartBeatTime() + { + return this.lastHeartBeatTime; + } + +/** +* if the trunk server +* @return true for the trunk server, otherwise false +*/ + public boolean isTrunkServer() + { + return this.ifTrunkServer; + } + +/** +* set fields +* @param bs byte array +* @param offset start offset +*/ + public void setFields(byte[] bs, int offset) + { + this.status = byteValue(bs, offset, fieldsArray[FIELD_INDEX_STATUS]); + this.id = stringValue(bs, offset, fieldsArray[FIELD_INDEX_ID]); + this.ipAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_IP_ADDR]); + this.srcIpAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_SRC_IP_ADDR]); + this.domainName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_DOMAIN_NAME]); + this.version = stringValue(bs, offset, fieldsArray[FIELD_INDEX_VERSION]); + this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]); + this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]); + this.uploadPriority = intValue(bs, offset, fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY]); + this.joinTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_JOIN_TIME]); + this.upTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_UP_TIME]); + this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]); + this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]); + this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]); + this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]); + this.currentWritePath = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH]); + + this.connectionAllocCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT]); + this.connectionCurrentCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT]); + this.connectionMaxCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT]); + + this.totalUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT]); + this.successUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT]); + this.totalAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT]); + this.successAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT]); + this.totalModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT]); + this.successModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT]); + this.totalTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT]); + this.successTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT]); + this.totalSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT]); + this.successSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT]); + this.totalDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT]); + this.successDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT]); + this.totalDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT]); + this.successDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT]); + this.totalGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT]); + this.successGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT]); + this.totalCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT]); + this.successCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT]); + this.totalDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT]); + this.successDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT]); + this.totalUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES]); + this.successUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES]); + this.totalAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES]); + this.successAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES]); + this.totalModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES]); + this.successModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES]); + this.totalDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES]); + this.successDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES]); + this.totalSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES]); + this.successSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES]); + this.totalSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES]); + this.successSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES]); + this.totalFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT]); + this.successFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT]); + this.totalFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT]); + this.successFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT]); + this.totalFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT]); + this.successFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT]); + this.lastSourceUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE]); + this.lastSyncUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE]); + this.lastSyncedTimestamp = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP]); + this.lastHeartBeatTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME]); + this.ifTrunkServer = booleanValue(bs, offset, fieldsArray[FIELD_INDEX_IF_TRUNK_FILE]); + } + +/** +* get fields total size +* @return fields total size +*/ + public static int getFieldsTotalSize() + { + return fieldsTotalSize; + } +} diff --git a/src/org/csource/fastdfs/TrackerClient.java b/src/org/csource/fastdfs/TrackerClient.java new file mode 100644 index 0000000..0e32113 --- /dev/null +++ b/src/org/csource/fastdfs/TrackerClient.java @@ -0,0 +1,990 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.net.Socket; + +/** +* Tracker client +* @author Happy Fish / YuQing +* @version Version 1.19 +*/ +public class TrackerClient +{ + protected TrackerGroup tracker_group; + protected byte errno; + +/** +* constructor with global tracker group +*/ + public TrackerClient() + { + this.tracker_group = ClientGlobal.g_tracker_group; + } + +/** +* constructor with specified tracker group +* @param tracker_group the tracker group object +*/ + public TrackerClient(TrackerGroup tracker_group) + { + this.tracker_group = tracker_group; + } + +/** +* get the error code of last call +* @return the error code of last call +*/ + public byte getErrorCode() + { + return this.errno; + } + + /** + * get a connection to tracker server + * @return tracker server Socket object, return null if fail + */ + public TrackerServer getConnection() throws IOException + { + return this.tracker_group.getConnection(); + } + + /** + * query storage server to upload file + * @param trackerServer the tracker server + * @return storage server Socket object, return null if fail + */ + public StorageServer getStoreStorage(TrackerServer trackerServer) throws IOException + { + final String groupName = null; + return this.getStoreStorage(trackerServer, groupName); + } + + /** + * query storage server to upload file + * @param trackerServer the tracker server + * @param groupName the group name to upload file to, can be empty + * @return storage server object, return null if fail + */ + public StorageServer getStoreStorage(TrackerServer trackerServer, String groupName) throws IOException + { + byte[] header; + String ip_addr; + int port; + byte cmd; + int out_len; + boolean bNewConnection; + byte store_path; + Socket trackerSocket; + + if (trackerServer == null) + { + trackerServer = getConnection(); + if (trackerServer == null) + { + return null; + } + bNewConnection = true; + } + else + { + bNewConnection = false; + } + + trackerSocket = trackerServer.getSocket(); + OutputStream out = trackerSocket.getOutputStream(); + + try + { + if (groupName == null || groupName.length() == 0) + { + cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE; + out_len = 0; + } + else + { + cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE; + out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + } + header = ProtoCommon.packHeader(cmd, out_len, (byte)0); + out.write(header); + + if (groupName != null && groupName.length() > 0) + { + byte[] bGroupName; + byte[] bs; + int group_len; + + bs = groupName.getBytes(ClientGlobal.g_charset); + bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + + if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) + { + group_len = bs.length; + } + else + { + group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + } + Arrays.fill(bGroupName, (byte)0); + System.arraycopy(bs, 0, bGroupName, 0, group_len); + out.write(bGroupName); + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(), + ProtoCommon.TRACKER_PROTO_CMD_RESP, + ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE-1).trim(); + + port = (int)ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + + ProtoCommon.FDFS_IPADDR_SIZE-1); + store_path = pkgInfo.body[ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN - 1]; + + return new StorageServer(ip_addr, port, store_path); + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + trackerServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + } + + throw ex; + } + finally + { + if (bNewConnection) + { + try + { + trackerServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + } + } + } + + /** + * query storage servers to upload file + * @param trackerServer the tracker server + * @param groupName the group name to upload file to, can be empty + * @return storage servers, return null if fail + */ + public StorageServer[] getStoreStorages(TrackerServer trackerServer, String groupName) throws IOException + { + byte[] header; + String ip_addr; + int port; + byte cmd; + int out_len; + boolean bNewConnection; + Socket trackerSocket; + + if (trackerServer == null) + { + trackerServer = getConnection(); + if (trackerServer == null) + { + return null; + } + bNewConnection = true; + } + else + { + bNewConnection = false; + } + + trackerSocket = trackerServer.getSocket(); + OutputStream out = trackerSocket.getOutputStream(); + + try + { + if (groupName == null || groupName.length() == 0) + { + cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL; + out_len = 0; + } + else + { + cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL; + out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + } + header = ProtoCommon.packHeader(cmd, out_len, (byte)0); + out.write(header); + + if (groupName != null && groupName.length() > 0) + { + byte[] bGroupName; + byte[] bs; + int group_len; + + bs = groupName.getBytes(ClientGlobal.g_charset); + bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + + if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) + { + group_len = bs.length; + } + else + { + group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + } + Arrays.fill(bGroupName, (byte)0); + System.arraycopy(bs, 0, bGroupName, 0, group_len); + out.write(bGroupName); + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(), + ProtoCommon.TRACKER_PROTO_CMD_RESP, -1); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN) + { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } + + int ipPortLen = pkgInfo.body.length - (ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1); + final int recordLength = ProtoCommon.FDFS_IPADDR_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; + + if (ipPortLen % recordLength != 0) + { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } + + int serverCount = ipPortLen / recordLength; + if (serverCount > 16) + { + this.errno = ProtoCommon.ERR_NO_ENOSPC; + return null; + } + + StorageServer[] results = new StorageServer[serverCount]; + byte store_path = pkgInfo.body[pkgInfo.body.length - 1]; + int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + + for (int i=0; i decoder = new ProtoStructDecoder(); + return decoder.decode(pkgInfo.body, StructGroupStat.class, StructGroupStat.getFieldsTotalSize()); + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + trackerServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + } + + throw ex; + } + catch(Exception ex) + { + ex.printStackTrace(); + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } + finally + { + if (bNewConnection) + { + try + { + trackerServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + } + } + } + + /** + * query storage server stat info of the group + * @param trackerServer the tracker server + * @param groupName the group name of storage server + * @return storage server stat array, return null if fail + */ + public StructStorageStat[] listStorages(TrackerServer trackerServer, String groupName) throws IOException + { + final String storageIpAddr = null; + return this.listStorages(trackerServer, groupName, storageIpAddr); + } + + /** + * query storage server stat info of the group + * @param trackerServer the tracker server + * @param groupName the group name of storage server + * @param storageIpAddr the storage server ip address, can be null or empty + * @return storage server stat array, return null if fail + */ + public StructStorageStat[] listStorages(TrackerServer trackerServer, + String groupName, String storageIpAddr) throws IOException + { + byte[] header; + byte[] bGroupName; + byte[] bs; + int len; + boolean bNewConnection; + Socket trackerSocket; + + if (trackerServer == null) + { + trackerServer = getConnection(); + if (trackerServer == null) + { + return null; + } + bNewConnection = true; + } + else + { + bNewConnection = false; + } + trackerSocket = trackerServer.getSocket(); + OutputStream out = trackerSocket.getOutputStream(); + + try + { + bs = groupName.getBytes(ClientGlobal.g_charset); + bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + + if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) + { + len = bs.length; + } + else + { + len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + } + Arrays.fill(bGroupName, (byte)0); + System.arraycopy(bs, 0, bGroupName, 0, len); + + int ipAddrLen; + byte[] bIpAddr; + if (storageIpAddr != null && storageIpAddr.length() > 0) + { + bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset); + if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) + { + ipAddrLen = bIpAddr.length; + } + else + { + ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1; + } + } + else + { + bIpAddr = null; + ipAddrLen = 0; + } + + header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte)0); + byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length); + if (ipAddrLen > 0) + { + System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen); + } + out.write(wholePkg); + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(), + ProtoCommon.TRACKER_PROTO_CMD_RESP, -1); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) + { + return null; + } + + ProtoStructDecoder decoder = new ProtoStructDecoder(); + return decoder.decode(pkgInfo.body, StructStorageStat.class, StructStorageStat.getFieldsTotalSize()); + } + catch(IOException ex) + { + if (!bNewConnection) + { + try + { + trackerServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + } + + throw ex; + } + catch(Exception ex) + { + ex.printStackTrace(); + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } + finally + { + if (bNewConnection) + { + try + { + trackerServer.close(); + } + catch(IOException ex1) + { + ex1.printStackTrace(); + } + } + } + } + + /** + * delete a storage server from the tracker server + * @param trackerServer the connected tracker server + * @param groupName the group name of storage server + * @param storageIpAddr the storage server ip address + * @return true for success, false for fail + */ + private boolean deleteStorage(TrackerServer trackerServer, + String groupName, String storageIpAddr) throws IOException + { + byte[] header; + byte[] bGroupName; + byte[] bs; + int len; + Socket trackerSocket; + + trackerSocket = trackerServer.getSocket(); + OutputStream out = trackerSocket.getOutputStream(); + + bs = groupName.getBytes(ClientGlobal.g_charset); + bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN]; + + if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) + { + len = bs.length; + } + else + { + len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN; + } + Arrays.fill(bGroupName, (byte)0); + System.arraycopy(bs, 0, bGroupName, 0, len); + + int ipAddrLen; + byte[] bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset); + if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) + { + ipAddrLen = bIpAddr.length; + } + else + { + ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1; + } + + header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte)0); + byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length); + System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen); + out.write(wholePkg); + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(), + ProtoCommon.TRACKER_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + return pkgInfo.errno == 0; + } + + /** + * delete a storage server from the global FastDFS cluster + * @param groupName the group name of storage server + * @param storageIpAddr the storage server ip address + * @return true for success, false for fail + */ + public boolean deleteStorage(String groupName, String storageIpAddr) throws IOException + { + return this.deleteStorage(ClientGlobal.g_tracker_group, groupName, storageIpAddr); + } + + /** + * delete a storage server from the FastDFS cluster + * @param trackerGroup the tracker server group + * @param groupName the group name of storage server + * @param storageIpAddr the storage server ip address + * @return true for success, false for fail + */ + public boolean deleteStorage(TrackerGroup trackerGroup, + String groupName, String storageIpAddr) throws IOException + { + int serverIndex; + int notFoundCount; + TrackerServer trackerServer; + + notFoundCount = 0; + for (serverIndex=0; serverIndex= this.tracker_servers.length) + { + this.tracker_server_index = 0; + } + + current_index = this.tracker_server_index; + } + + try + { + return this.getConnection(current_index); + } + catch(IOException ex) + { + System.err.println("connect to server " + this.tracker_servers[current_index].getAddress().getHostAddress() + ":" + this.tracker_servers[current_index].getPort() + " fail"); + ex.printStackTrace(System.err); + } + + for (int i=0; i 0) + { + try + { + if ((bytes=inputStream.read(buff, 0, remainBytes > buff.length ? buff.length : (int)remainBytes)) < 0) + { + return -1; + } + } + catch(IOException ex) + { + ex.printStackTrace(); + return -1; + } + + out.write(buff, 0, bytes); + remainBytes -= bytes; + } + + return 0; + } +} diff --git a/src/org/csource/fastdfs/test/DownloadFileWriter.java b/src/org/csource/fastdfs/test/DownloadFileWriter.java new file mode 100644 index 0000000..516ec2d --- /dev/null +++ b/src/org/csource/fastdfs/test/DownloadFileWriter.java @@ -0,0 +1,69 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.util.*; +import java.net.*; +import org.csource.fastdfs.*; + +/** +* DowloadCallback test +* @author Happy Fish / YuQing +* @version Version 1.3 +*/ +public class DownloadFileWriter implements DownloadCallback +{ + private String filename; + private FileOutputStream out = null; + private long current_bytes = 0; + + public DownloadFileWriter(String filename) + { + this.filename = filename; + } + + public int recv(long file_size, byte[] data, int bytes) + { + try + { + if (this.out == null) + { + this.out = new FileOutputStream(this.filename); + } + + this.out.write(data, 0, bytes); + this.current_bytes += bytes; + + if (this.current_bytes == file_size) + { + this.out.close(); + this.out = null; + this.current_bytes = 0; + } + } + catch(IOException ex) + { + ex.printStackTrace(); + return -1; + } + + return 0; + } + + protected void finalize() throws Throwable + { + if (this.out != null) + { + this.out.close(); + this.out = null; + } + } +} + diff --git a/src/org/csource/fastdfs/test/Monitor.java b/src/org/csource/fastdfs/test/Monitor.java new file mode 100644 index 0000000..e2d8cff --- /dev/null +++ b/src/org/csource/fastdfs/test/Monitor.java @@ -0,0 +1,222 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.text.SimpleDateFormat; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* load test class +* @author Happy Fish / YuQing +* @version Version 1.20 +*/ +public class Monitor +{ + private Monitor() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ */ + public static void main(String args[]) + { + if (args.length < 1) + { + System.out.println("Error: Must have 1 parameter: config filename"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + try + { + ClientGlobal.init(args[0]); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + TrackerClient tracker = new TrackerClient(); + + /* + System.out.println("delete storage return: " + tracker.deleteStorage("group1", "192.168.0.192")); + System.out.println("delete storage errno: " + tracker.getErrorCode()); + */ + + TrackerServer trackerServer = tracker.getConnection(); + if (trackerServer == null) + { + return; + } + + int count; + StructGroupStat[] groupStats = tracker.listGroups(trackerServer); + if (groupStats == null) + { + System.out.println(""); + System.out.println("ERROR! list groups error, error no: " + tracker.getErrorCode()); + System.out.println(""); + return; + } + + System.out.println("group count: " + groupStats.length); + + count = 0; + for (StructGroupStat groupStat : groupStats) + { + count++; + System.out.println("Group " + count + ":"); + System.out.println("group name = " + groupStat.getGroupName()); + System.out.println("disk total space = " + groupStat.getTotalMB() + "MB"); + System.out.println("disk free space = " + groupStat.getFreeMB() + " MB"); + System.out.println("trunk free space = " + groupStat.getTrunkFreeMB() + " MB"); + System.out.println("storage server count = " + groupStat.getStorageCount()); + System.out.println("active server count = " + groupStat.getActiveCount()); + System.out.println("storage server port = " + groupStat.getStoragePort()); + System.out.println("storage HTTP port = " + groupStat.getStorageHttpPort()); + System.out.println("store path count = " + groupStat.getStorePathCount()); + System.out.println("subdir count per path = " + groupStat.getSubdirCountPerPath()); + System.out.println("current write server index = " + groupStat.getCurrentWriteServer()); + System.out.println("current trunk file id = " + groupStat.getCurrentTrunkFileId()); + + StructStorageStat[] storageStats = tracker.listStorages(trackerServer, groupStat.getGroupName()); + if (storageStats == null) + { + System.out.println(""); + System.out.println("ERROR! list storage error, error no: " + tracker.getErrorCode()); + System.out.println(""); + break; + } + + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + int stroageCount = 0; + for (StructStorageStat storageStat : storageStats) + { + stroageCount++; + System.out.println("\tStorage " + stroageCount + ":"); + System.out.println("\t\tstorage id = " + storageStat.getId()); + System.out.println("\t\tip_addr = " + storageStat.getIpAddr() + " " + ProtoCommon.getStorageStatusCaption(storageStat.getStatus())); + System.out.println("\t\thttp domain = " + storageStat.getDomainName()); + System.out.println("\t\tversion = " + storageStat.getVersion()); + System.out.println("\t\tjoin time = " + df.format(storageStat.getJoinTime())); + System.out.println("\t\tup time = " + (storageStat.getUpTime().getTime() == 0 ? "" : df.format(storageStat.getUpTime()))); + System.out.println("\t\ttotal storage = " + storageStat.getTotalMB() + "MB"); + System.out.println("\t\tfree storage = " + storageStat.getFreeMB() + "MB"); + System.out.println("\t\tupload priority = " + storageStat.getUploadPriority()); + System.out.println("\t\tstore_path_count = " + storageStat.getStorePathCount()); + System.out.println("\t\tsubdir_count_per_path = " + storageStat.getSubdirCountPerPath()); + System.out.println("\t\tstorage_port = " + storageStat.getStoragePort()); + System.out.println("\t\tstorage_http_port = " + storageStat.getStorageHttpPort()); + System.out.println("\t\tcurrent_write_path = " + storageStat.getCurrentWritePath()); + System.out.println("\t\tsource ip_addr = " + storageStat.getSrcIpAddr()); + System.out.println("\t\tif_trunk_server = " + storageStat.isTrunkServer()); + System.out.println("\t\ttotal_upload_count = " + storageStat.getTotalUploadCount()); + System.out.println("\t\tsuccess_upload_count = " + storageStat.getSuccessUploadCount()); + System.out.println("\t\ttotal_append_count = " + storageStat.getTotalAppendCount()); + System.out.println("\t\tsuccess_append_count = " + storageStat.getSuccessAppendCount()); + System.out.println("\t\ttotal_modify_count = " + storageStat.getTotalModifyCount()); + System.out.println("\t\tsuccess_modify_count = " + storageStat.getSuccessModifyCount()); + System.out.println("\t\ttotal_truncate_count = " + storageStat.getTotalTruncateCount()); + System.out.println("\t\tsuccess_truncate_count = " + storageStat.getSuccessTruncateCount()); + System.out.println("\t\ttotal_set_meta_count = " + storageStat.getTotalSetMetaCount()); + System.out.println("\t\tsuccess_set_meta_count = " + storageStat.getSuccessSetMetaCount()); + System.out.println("\t\ttotal_delete_count = " + storageStat.getTotalDeleteCount()); + System.out.println("\t\tsuccess_delete_count = " + storageStat.getSuccessDeleteCount()); + System.out.println("\t\ttotal_download_count = " + storageStat.getTotalDownloadCount()); + System.out.println("\t\tsuccess_download_count = " + storageStat.getSuccessDownloadCount()); + System.out.println("\t\ttotal_get_meta_count = " + storageStat.getTotalGetMetaCount()); + System.out.println("\t\tsuccess_get_meta_count = " + storageStat.getSuccessGetMetaCount()); + System.out.println("\t\ttotal_create_link_count = " + storageStat.getTotalCreateLinkCount()); + System.out.println("\t\tsuccess_create_link_count = " + storageStat.getSuccessCreateLinkCount()); + System.out.println("\t\ttotal_delete_link_count = " + storageStat.getTotalDeleteLinkCount()); + System.out.println("\t\tsuccess_delete_link_count = " + storageStat.getSuccessDeleteLinkCount()); + System.out.println("\t\ttotal_upload_bytes = " + storageStat.getTotalUploadBytes()); + System.out.println("\t\tsuccess_upload_bytes = " + storageStat.getSuccessUploadBytes()); + System.out.println("\t\ttotal_append_bytes = " + storageStat.getTotalAppendBytes()); + System.out.println("\t\tsuccess_append_bytes = " + storageStat.getSuccessAppendBytes()); + System.out.println("\t\ttotal_modify_bytes = " + storageStat.getTotalModifyBytes()); + System.out.println("\t\tsuccess_modify_bytes = " + storageStat.getSuccessModifyBytes()); + System.out.println("\t\ttotal_download_bytes = " + storageStat.getTotalDownloadloadBytes()); + System.out.println("\t\tsuccess_download_bytes = " + storageStat.getSuccessDownloadloadBytes()); + System.out.println("\t\ttotal_sync_in_bytes = " + storageStat.getTotalSyncInBytes()); + System.out.println("\t\tsuccess_sync_in_bytes = " + storageStat.getSuccessSyncInBytes()); + System.out.println("\t\ttotal_sync_out_bytes = " + storageStat.getTotalSyncOutBytes()); + System.out.println("\t\tsuccess_sync_out_bytes = " + storageStat.getSuccessSyncOutBytes()); + System.out.println("\t\ttotal_file_open_count = " + storageStat.getTotalFileOpenCount()); + System.out.println("\t\tsuccess_file_open_count = " + storageStat.getSuccessFileOpenCount()); + System.out.println("\t\ttotal_file_read_count = " + storageStat.getTotalFileReadCount()); + System.out.println("\t\tsuccess_file_read_count = " + storageStat.getSuccessFileReadCount()); + System.out.println("\t\ttotal_file_write_count = " + storageStat.getTotalFileWriteCount()); + System.out.println("\t\tsuccess_file_write_count = " + storageStat.getSuccessFileWriteCount()); + System.out.println("\t\tlast_heart_beat_time = " + df.format(storageStat.getLastHeartBeatTime())); + System.out.println("\t\tlast_source_update = " + df.format(storageStat.getLastSourceUpdate())); + System.out.println("\t\tlast_sync_update = " + df.format(storageStat.getLastSyncUpdate())); + System.out.println("\t\tlast_synced_timestamp = " + df.format(storageStat.getLastSyncedTimestamp()) + getSyncedDelayString(storageStats, storageStat)); + } + } + + trackerServer.close(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } + + protected static String getSyncedDelayString(StructStorageStat[] storageStats, StructStorageStat currentStorageStat) + { + long maxLastSourceUpdate = 0; + for (StructStorageStat storageStat : storageStats) + { + if (storageStat != currentStorageStat && storageStat.getLastSourceUpdate().getTime() > maxLastSourceUpdate) + { + maxLastSourceUpdate = storageStat.getLastSourceUpdate().getTime(); + } + } + + if (maxLastSourceUpdate == 0) + { + return ""; + } + + if (currentStorageStat.getLastSyncedTimestamp().getTime() == 0) + { + return " (never synced)"; + } + + int delaySeconds = (int)((maxLastSourceUpdate - currentStorageStat.getLastSyncedTimestamp().getTime()) / 1000); + int day = delaySeconds / (24 * 3600); + int remainSeconds = delaySeconds % (24 * 3600); + int hour = remainSeconds / 3600; + remainSeconds %= 3600; + int minute = remainSeconds / 60; + int second = remainSeconds % 60; + String delayTimeStr; + if (day != 0) + { + delayTimeStr = String.format("%1$d days %2$02dh:%3$02dm:%4$02ds", day, hour, minute, second); + } + else if (hour != 0) + { + delayTimeStr = String.format("%1$02dh:%2$02dm:%3$02ds", hour, minute, second); + } + else if (minute != 0) + { + delayTimeStr = String.format("%1$02dm:%2$02ds", minute, second); + } + else + { + delayTimeStr = String.format("%1$ds", second); + } + + return " (" + delayTimeStr + " delay)"; + } +} diff --git a/src/org/csource/fastdfs/test/Test.java b/src/org/csource/fastdfs/test/Test.java new file mode 100644 index 0000000..13e403e --- /dev/null +++ b/src/org/csource/fastdfs/test/Test.java @@ -0,0 +1,77 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* client test +* @author Happy Fish / YuQing +* @version Version 1.18 +*/ +public class Test +{ + private Test() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ *
  • args[1]: local filename to upload
+ */ + public static void main(String args[]) + { + if (args.length < 2) + { + System.out.println("Error: Must have 2 parameters, one is config filename, " + + "the other is the local filename to upload"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + String conf_filename = args[0]; + String local_filename = args[1]; + + try + { + ClientGlobal.init(conf_filename); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + TrackerClient tracker = new TrackerClient(); + TrackerServer trackerServer = tracker.getConnection(); + StorageServer storageServer = null; + StorageClient1 client = new StorageClient1(trackerServer, storageServer); + + NameValuePair[] metaList = new NameValuePair[1]; + metaList[0] = new NameValuePair("fileName", local_filename); + String fileId = client.upload_file1(local_filename, null, metaList); + System.out.println("upload success. file id is: " + fileId); + + int i = 0; + while (i++ < 10) { + byte[] result = client.download_file1(fileId); + System.out.println(i + ", download result is: " + result.length); + } + + trackerServer.close(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/org/csource/fastdfs/test/Test1.java b/src/org/csource/fastdfs/test/Test1.java new file mode 100644 index 0000000..42a80a4 --- /dev/null +++ b/src/org/csource/fastdfs/test/Test1.java @@ -0,0 +1,50 @@ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +public class Test1 +{ + public static void main(String args[]) + { + try + { + ClientGlobal.init("fdfs_client.conf"); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + TrackerGroup tg = new TrackerGroup(new InetSocketAddress[]{new InetSocketAddress("192.168.0.196", 22122)}); + TrackerClient tc = new TrackerClient(tg); + + TrackerServer ts = tc.getConnection(); + if (ts == null) + { + System.out.println("getConnection return null"); + return; + } + + StorageServer ss = tc.getStoreStorage(ts); + if (ss == null) + { + System.out.println("getStoreStorage return null"); + } + + StorageClient1 sc1 = new StorageClient1(ts, ss); + + NameValuePair[] meta_list = null; //new NameValuePair[0]; + String item = "c:/windows/system32/notepad.exe"; + String fileid = sc1.upload_file1(item, "exe", meta_list); //此行异常 + + System.out.println("Upload local file "+item+" ok, fileid="+fileid); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + + } +} diff --git a/src/org/csource/fastdfs/test/TestAppender.java b/src/org/csource/fastdfs/test/TestAppender.java new file mode 100644 index 0000000..99e097d --- /dev/null +++ b/src/org/csource/fastdfs/test/TestAppender.java @@ -0,0 +1,358 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* client test +* @author Happy Fish / YuQing +* @version Version 1.20 +*/ +public class TestAppender +{ + private TestAppender() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ *
  • args[1]: local filename to upload
+ */ + public static void main(String args[]) + { + if (args.length < 2) + { + System.out.println("Error: Must have 2 parameters, one is config filename, " + + "the other is the local filename to upload"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + String conf_filename = args[0]; + String local_filename = args[1]; + + try + { + ClientGlobal.init(conf_filename); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + long startTime; + String group_name; + String remote_filename; + ServerInfo[] servers; + TrackerClient tracker = new TrackerClient(); + TrackerServer trackerServer = tracker.getConnection(); + + StorageServer storageServer = null; + + /* + storageServer = tracker.getStoreStorage(trackerServer); + if (storageServer == null) + { + System.out.println("getStoreStorage fail, error code: " + tracker.getErrorCode()); + return; + } + */ + + StorageClient client = new StorageClient(trackerServer, storageServer); + byte[] file_buff; + NameValuePair[] meta_list; + String[] results; + String appender_filename; + String file_ext_name; + int errno; + + meta_list = new NameValuePair[4]; + meta_list[0] = new NameValuePair("width", "800"); + meta_list[1] = new NameValuePair("heigth", "600"); + meta_list[2] = new NameValuePair("bgcolor", "#FFFFFF"); + meta_list[3] = new NameValuePair("author", "Mike"); + + file_buff = "this is a test".getBytes(ClientGlobal.g_charset); + System.out.println("file length: " + file_buff.length); + + group_name = null; + StorageServer[] storageServers = tracker.getStoreStorages(trackerServer, group_name); + if (storageServers == null) + { + System.err.println("get store storage servers fail, error code: " + tracker.getErrorCode()); + } + else + { + System.err.println("store storage servers count: " + storageServers.length); + for (int k=0; k 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + file_ext_name = local_filename.substring(nPos+1); + } + else + { + file_ext_name = null; + } + + results = client.upload_appender_file(null, f.length(), + new UploadLocalFileSender(local_filename), file_ext_name, meta_list); + if (results != null) + { + group_name = results[0]; + remote_filename = results[1]; + + System.out.println("group name: " + group_name + ", remote filename: " + remote_filename); + System.out.println(client.get_file_info(group_name, remote_filename)); + + appender_filename = remote_filename; + startTime = System.currentTimeMillis(); + errno = client.append_file(group_name, appender_filename, f.length(), new UploadLocalFileSender(local_filename)); + System.out.println("append_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (errno == 0) + { + System.err.println(client.get_file_info(group_name, appender_filename)); + } + else + { + System.err.println("append file fail, error no: " + errno); + } + + startTime = System.currentTimeMillis(); + errno = client.modify_file(group_name, appender_filename, 0, f.length(), new UploadLocalFileSender(local_filename)); + System.out.println("modify_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (errno == 0) + { + System.err.println(client.get_file_info(group_name, appender_filename)); + } + else + { + System.err.println("modify file fail, error no: " + errno); + } + + startTime = System.currentTimeMillis(); + errno = client.truncate_file(group_name, appender_filename); + System.out.println("truncate_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (errno == 0) + { + System.err.println(client.get_file_info(group_name, appender_filename)); + } + else + { + System.err.println("truncate file fail, error no: " + errno); + } + } + else + { + System.err.println("Upload file fail, error no: " + errno); + } + + storageServer = tracker.getFetchStorage(trackerServer, group_name, remote_filename); + if (storageServer == null) + { + System.out.println("getFetchStorage fail, errno code: " + tracker.getErrorCode()); + return; + } + /* for test only */ + System.out.println("active test to storage server: " + ProtoCommon.activeTest(storageServer.getSocket())); + + storageServer.close(); + + /* for test only */ + System.out.println("active test to tracker server: " + ProtoCommon.activeTest(trackerServer.getSocket())); + + trackerServer.close(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/org/csource/fastdfs/test/TestAppender1.java b/src/org/csource/fastdfs/test/TestAppender1.java new file mode 100644 index 0000000..393cad7 --- /dev/null +++ b/src/org/csource/fastdfs/test/TestAppender1.java @@ -0,0 +1,338 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* client test +* @author Happy Fish / YuQing +* @version Version 1.20 +*/ +public class TestAppender1 +{ + private TestAppender1() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ *
  • args[1]: local filename to upload
+ */ + public static void main(String args[]) + { + if (args.length < 2) + { + System.out.println("Error: Must have 2 parameters, one is config filename, " + + "the other is the local filename to upload"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + String conf_filename = args[0]; + String local_filename = args[1]; + + try + { + ClientGlobal.init(conf_filename); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + long startTime; + ServerInfo[] servers; + TrackerClient tracker = new TrackerClient(); + TrackerServer trackerServer = tracker.getConnection(); + + StorageServer storageServer = null; + + /* + storageServer = tracker.getStoreStorage(trackerServer); + if (storageServer == null) + { + System.out.println("getStoreStorage fail, error code: " + tracker.getErrorCode()); + return; + } + */ + + StorageClient1 client = new StorageClient1(trackerServer, storageServer); + byte[] file_buff; + NameValuePair[] meta_list; + String group_name; + String appender_file_id; + String file_ext_name; + int errno; + + meta_list = new NameValuePair[4]; + meta_list[0] = new NameValuePair("width", "800"); + meta_list[1] = new NameValuePair("heigth", "600"); + meta_list[2] = new NameValuePair("bgcolor", "#FFFFFF"); + meta_list[3] = new NameValuePair("author", "Mike"); + + file_buff = "this is a test".getBytes(ClientGlobal.g_charset); + System.out.println("file length: " + file_buff.length); + + group_name = null; + StorageServer[] storageServers = tracker.getStoreStorages(trackerServer, group_name); + if (storageServers == null) + { + System.err.println("get store storage servers fail, error code: " + tracker.getErrorCode()); + } + else + { + System.err.println("store storage servers count: " + storageServers.length); + for (int k=0; k 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + file_ext_name = local_filename.substring(nPos+1); + } + else + { + file_ext_name = null; + } + + appender_file_id = client.upload_appender_file1(null, f.length(), + new UploadLocalFileSender(local_filename), file_ext_name, meta_list); + if (appender_file_id != null) + { + System.out.println(client.get_file_info1(appender_file_id)); + + startTime = System.currentTimeMillis(); + errno = client.append_file1(appender_file_id, f.length(), new UploadLocalFileSender(local_filename)); + System.out.println("append_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (errno == 0) + { + System.err.println(client.get_file_info1(appender_file_id)); + } + else + { + System.err.println("append file fail, error no: " + errno); + } + + startTime = System.currentTimeMillis(); + errno = client.modify_file1(appender_file_id, 0, f.length(), new UploadLocalFileSender(local_filename)); + System.out.println("modify_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (errno == 0) + { + System.err.println(client.get_file_info1(appender_file_id)); + } + else + { + System.err.println("modify file fail, error no: " + errno); + } + + startTime = System.currentTimeMillis(); + errno = client.truncate_file1(appender_file_id, 0); + System.out.println("truncate_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (errno == 0) + { + System.err.println(client.get_file_info1(appender_file_id)); + } + else + { + System.err.println("truncate file fail, error no: " + errno); + } + } + else + { + System.err.println("Upload file fail, error no: " + errno); + } + + storageServer = tracker.getFetchStorage1(trackerServer, appender_file_id); + if (storageServer == null) + { + System.out.println("getFetchStorage fail, errno code: " + tracker.getErrorCode()); + return; + } + /* for test only */ + System.out.println("active test to storage server: " + ProtoCommon.activeTest(storageServer.getSocket())); + + storageServer.close(); + + /* for test only */ + System.out.println("active test to tracker server: " + ProtoCommon.activeTest(trackerServer.getSocket())); + + trackerServer.close(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/org/csource/fastdfs/test/TestClient.java b/src/org/csource/fastdfs/test/TestClient.java new file mode 100644 index 0000000..06afe38 --- /dev/null +++ b/src/org/csource/fastdfs/test/TestClient.java @@ -0,0 +1,351 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* client test +* @author Happy Fish / YuQing +* @version Version 1.18 +*/ +public class TestClient +{ + private TestClient() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ *
  • args[1]: local filename to upload
+ */ + public static void main(String args[]) + { + if (args.length < 2) + { + System.out.println("Error: Must have 2 parameters, one is config filename, " + + "the other is the local filename to upload"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + String conf_filename = args[0]; + String local_filename = args[1]; + + try + { + ClientGlobal.init(conf_filename); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + long startTime; + String group_name; + String remote_filename; + ServerInfo[] servers; + TrackerClient tracker = new TrackerClient(); + TrackerServer trackerServer = tracker.getConnection(); + + StorageServer storageServer = null; + + /* + storageServer = tracker.getStoreStorage(trackerServer); + if (storageServer == null) + { + System.out.println("getStoreStorage fail, error code: " + tracker.getErrorCode()); + return; + } + */ + + StorageClient client = new StorageClient(trackerServer, storageServer); + byte[] file_buff; + NameValuePair[] meta_list; + String[] results; + String master_filename; + String prefix_name; + String file_ext_name; + String generated_slave_filename; + int errno; + + meta_list = new NameValuePair[4]; + meta_list[0] = new NameValuePair("width", "800"); + meta_list[1] = new NameValuePair("heigth", "600"); + meta_list[2] = new NameValuePair("bgcolor", "#FFFFFF"); + meta_list[3] = new NameValuePair("author", "Mike"); + + file_buff = "this is a test".getBytes(ClientGlobal.g_charset); + System.out.println("file length: " + file_buff.length); + + group_name = null; + StorageServer[] storageServers = tracker.getStoreStorages(trackerServer, group_name); + if (storageServers == null) + { + System.err.println("get store storage servers fail, error code: " + tracker.getErrorCode()); + } + else + { + System.err.println("store storage servers count: " + storageServers.length); + for (int k=0; k 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + file_ext_name = local_filename.substring(nPos+1); + } + else + { + file_ext_name = null; + } + + results = client.upload_file(null, f.length(), + new UploadLocalFileSender(local_filename), file_ext_name, meta_list); + if (results != null) + { + group_name = results[0]; + remote_filename = results[1]; + + System.out.println("group name: " + group_name + ", remote filename: " + remote_filename); + System.out.println(client.get_file_info(group_name, remote_filename)); + + master_filename = remote_filename; + prefix_name = "-part3"; + startTime = System.currentTimeMillis(); + results = client.upload_file(group_name, master_filename, prefix_name, f.length(), new UploadLocalFileSender(local_filename), file_ext_name, meta_list); + System.out.println("upload_file time used: " + (System.currentTimeMillis() - startTime) + " ms"); + if (results != null) + { + System.err.println("slave file group_name: " + results[0] + ", remote_filename: " + results[1]); + + generated_slave_filename = ProtoCommon.genSlaveFilename(master_filename, prefix_name, file_ext_name); + if (!generated_slave_filename.equals(results[1])) + { + System.err.println("generated slave file: " + generated_slave_filename + "\n != returned slave file: " + results[1]); + } + + System.err.println(client.get_file_info(results[0], results[1])); + } + } + else + { + System.err.println("Upload file fail, error no: " + errno); + } + + storageServer = tracker.getFetchStorage(trackerServer, group_name, remote_filename); + if (storageServer == null) + { + System.out.println("getFetchStorage fail, errno code: " + tracker.getErrorCode()); + return; + } + /* for test only */ + System.out.println("active test to storage server: " + ProtoCommon.activeTest(storageServer.getSocket())); + + storageServer.close(); + + /* for test only */ + System.out.println("active test to tracker server: " + ProtoCommon.activeTest(trackerServer.getSocket())); + + trackerServer.close(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/org/csource/fastdfs/test/TestClient1.java b/src/org/csource/fastdfs/test/TestClient1.java new file mode 100644 index 0000000..9b47cbb --- /dev/null +++ b/src/org/csource/fastdfs/test/TestClient1.java @@ -0,0 +1,313 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* client test +* @author Happy Fish / YuQing +* @version Version 1.16 +*/ +public class TestClient1 +{ + private TestClient1() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ *
  • args[1]: local filename to upload
+ */ + public static void main(String args[]) + { + if (args.length < 2) + { + System.out.println("Error: Must have 2 parameters, one is config filename, " + + "the other is the local filename to upload"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + String conf_filename = args[0]; + String local_filename = args[1]; + String group_name; + + try + { + ClientGlobal.init(conf_filename); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + String file_id; + + TrackerClient tracker = new TrackerClient(); + TrackerServer trackerServer = tracker.getConnection(); + + StorageServer storageServer = null; + /* + storageServer = tracker.getStoreStorage(trackerServer); + if (storageServer == null) + { + System.out.println("getStoreStorage fail, error code: " + tracker.getErrorCode()); + return; + } + */ + StorageClient1 client = new StorageClient1(trackerServer, storageServer); + byte[] file_buff; + NameValuePair[] meta_list; + String master_file_id; + String prefix_name; + String file_ext_name; + String slave_file_id; + String generated_slave_file_id; + int errno; + + group_name = "group1"; + StorageServer[] storageServers = tracker.getStoreStorages(trackerServer, group_name); + if (storageServers == null) + { + System.err.println("get store storage servers fail, error code: " + tracker.getErrorCode()); + } + else + { + System.err.println("store storage servers count: " + storageServers.length); + for (int k=0; k 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) + { + file_ext_name = local_filename.substring(nPos+1); + } + else + { + file_ext_name = null; + } + + file_id = client.upload_file1(null, f.length(), new UploadLocalFileSender(local_filename), file_ext_name, meta_list); + if (file_id != null) + { + System.out.println("file id: " + file_id); + System.out.println(client.get_file_info1(file_id)); + master_file_id = file_id; + prefix_name = "-part3"; + slave_file_id = client.upload_file1(master_file_id, prefix_name, f.length(), new UploadLocalFileSender(local_filename), file_ext_name, meta_list); + if (slave_file_id != null) + { + System.err.println("slave file_id: " + slave_file_id); + generated_slave_file_id = ProtoCommon.genSlaveFilename(master_file_id, prefix_name, file_ext_name); + if (!generated_slave_file_id.equals(slave_file_id)) + { + System.err.println("generated slave file: " + generated_slave_file_id + "\n != returned slave file: " + slave_file_id); + } + } + } + else + { + System.err.println("Upload file fail, error no: " + errno); + } + + storageServer = tracker.getFetchStorage1(trackerServer, file_id); + if (storageServer == null) + { + System.out.println("getFetchStorage fail, errno code: " + tracker.getErrorCode()); + return; + } + + /* for test only */ + System.out.println("active test to storage server: " + ProtoCommon.activeTest(storageServer.getSocket())); + storageServer.close(); + + /* for test only */ + System.out.println("active test to tracker server: " + ProtoCommon.activeTest(trackerServer.getSocket())); + + trackerServer.close(); + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/org/csource/fastdfs/test/TestLoad.java b/src/org/csource/fastdfs/test/TestLoad.java new file mode 100644 index 0000000..7572379 --- /dev/null +++ b/src/org/csource/fastdfs/test/TestLoad.java @@ -0,0 +1,316 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.csource.common.*; +import org.csource.fastdfs.*; + +/** +* load test class +* @author Happy Fish / YuQing +* @version Version 1.11 +*/ +public class TestLoad +{ + public static java.util.concurrent.ConcurrentLinkedQueue file_ids; + public static int total_download_count = 0; + public static int success_download_count = 0; + public static int fail_download_count = 0; + public static int total_upload_count = 0; + public static int success_upload_count = 0; + public static int upload_thread_count = 0; + +/** +* discard file content callback class when download file +* @author Happy Fish / YuQing +* @version Version 1.0 +*/ + public static class DownloadFileDiscard implements DownloadCallback + { + public DownloadFileDiscard() + { + } + + public int recv(long file_size, byte[] data, int bytes) + { + return 0; + } + } + +/** +* file uploader +* @author Happy Fish / YuQing +* @version Version 1.0 +*/ + public static class Uploader + { + public TrackerClient tracker; + public TrackerServer trackerServer; + + public Uploader() throws Exception + { + this.tracker = new TrackerClient(); + this.trackerServer = tracker.getConnection(); + } + + public int uploadFile() throws Exception + { + StorageServer storageServer = null; + StorageClient1 client = new StorageClient1(trackerServer, storageServer); + byte[] file_buff; + String file_id; + + file_buff = new byte[2 * 1024]; + java.util.Arrays.fill(file_buff, (byte)65); + + try + { + file_id = client.upload_file1(file_buff, "txt", null); + if (file_id == null) + { + System.out.println("upload file fail, error code: " + client.getErrorCode()); + return -1; + } + + TestLoad.file_ids.offer(file_id); + return 0; + } + catch(Exception ex) + { + System.out.println("upload file fail, error mesg: " + ex.getMessage()); + return -1; + } + } + } + +/** +* file downloader +* @author Happy Fish / YuQing +* @version Version 1.0 +*/ + public static class Downloader + { + public TrackerClient tracker; + public TrackerServer trackerServer; + public DownloadFileDiscard callback; + + public Downloader() throws Exception + { + this.tracker = new TrackerClient(); + this.trackerServer = tracker.getConnection(); + this.callback = new DownloadFileDiscard(); + } + + public int downloadFile(String file_id) throws Exception + { + int errno; + StorageServer storageServer = null; + StorageClient1 client = new StorageClient1(trackerServer, storageServer); + + try + { + errno = client.download_file1(file_id, this.callback); + if (errno != 0) + { + System.out.println("Download file fail, file_id: " + file_id + ", error no: " + errno); + } + return errno; + } + catch(Exception ex) + { + System.out.println("Download file fail, error mesg: " + ex.getMessage()); + return -1; + } + } + } + +/** +* upload file thread +* @author Happy Fish / YuQing +* @version Version 1.0 +*/ + public static class UploadThread extends Thread + { + private int thread_index; + + public UploadThread(int index) + { + this.thread_index = index; + } + + public void run() + { + try + { + TestLoad.upload_thread_count++; + Uploader uploader = new Uploader(); + + System.out.println("upload thread " + this.thread_index + " start"); + + for (int i=0; i<50000; i++) + { + TestLoad.total_upload_count++; + if (uploader.uploadFile() == 0) + { + TestLoad.success_upload_count++; + } + } + } + catch(Exception ex) + { + ex.printStackTrace(); + } + finally + { + TestLoad.upload_thread_count--; + } + + System.out.println("upload thread " + this.thread_index + + " exit, total_upload_count: " + TestLoad.total_upload_count + + ", success_upload_count: " + TestLoad.success_upload_count + + ", total_download_count: " + TestLoad.total_download_count + + ", success_download_count: " + TestLoad.success_download_count); + } + } + +/** +* download file thread +* @author Happy Fish / YuQing +* @version Version 1.0 +*/ + public static class DownloadThread extends Thread + { + private int thread_index; + private static Integer counter_lock = new Integer(0); + + public DownloadThread(int index) + { + this.thread_index = index; + } + + public void run() + { + try + { + String file_id; + Downloader downloader = new Downloader(); + + System.out.println("download thread " + this.thread_index + " start"); + + file_id = ""; + while (TestLoad.upload_thread_count != 0 || file_id != null) + { + file_id = (String)TestLoad.file_ids.poll(); + if (file_id == null) + { + Thread.sleep(10); + continue; + } + + synchronized (this.counter_lock) + { + TestLoad.total_download_count++; + } + if (downloader.downloadFile(file_id) == 0) + { + synchronized (this.counter_lock) + { + TestLoad.success_download_count++; + } + } + else + { + TestLoad.fail_download_count++; + } + } + + for (int i=0; i<3 && TestLoad.total_download_count < TestLoad.total_upload_count; i++) + { + file_id = (String)TestLoad.file_ids.poll(); + if (file_id == null) + { + Thread.sleep(10); + continue; + } + + synchronized (this.counter_lock) + { + TestLoad.total_download_count++; + } + if (downloader.downloadFile(file_id) == 0) + { + synchronized (this.counter_lock) + { + TestLoad.success_download_count++; + } + } + else + { + TestLoad.fail_download_count++; + } + } + } + catch(Exception ex) + { + ex.printStackTrace(); + } + + System.out.println("download thread " + this.thread_index + + " exit, total_download_count: " + TestLoad.total_download_count + + ", success_download_count: " + TestLoad.success_download_count + + ", fail_download_count: " + TestLoad.fail_download_count); + } + } + + private TestLoad() + { + } + + /** + * entry point + * @param args comand arguments + *
  • args[0]: config filename
+ */ + public static void main(String args[]) + { + if (args.length < 1) + { + System.out.println("Error: Must have 1 parameter: config filename"); + return; + } + + System.out.println("java.version=" + System.getProperty("java.version")); + + try + { + ClientGlobal.init(args[0]); + System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); + System.out.println("charset=" + ClientGlobal.g_charset); + + file_ids = new java.util.concurrent.ConcurrentLinkedQueue(); + + for (int i=0; i<10; i++) + { + (new UploadThread(i)).start(); + } + + for (int i=0; i<20; i++) + { + (new DownloadThread(i)).start(); + } + } + catch(Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/org/csource/fastdfs/test/UploadLocalFileSender.java b/src/org/csource/fastdfs/test/UploadLocalFileSender.java new file mode 100644 index 0000000..f5ccbd6 --- /dev/null +++ b/src/org/csource/fastdfs/test/UploadLocalFileSender.java @@ -0,0 +1,61 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS Java Client may be copied only under the terms of the GNU Lesser +* General Public License (LGPL). +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +*/ + +package org.csource.fastdfs.test; + +import java.io.*; +import java.util.*; +import java.net.*; +import org.csource.fastdfs.*; + +/** +* upload file callback class, local file sender +* @author Happy Fish / YuQing +* @version Version 1.0 +*/ +public class UploadLocalFileSender implements UploadCallback +{ + private String local_filename; + + public UploadLocalFileSender(String szLocalFilename) + { + this.local_filename = szLocalFilename; + } + + /** + * send file content callback function, be called only once when the file uploaded + * @param out output stream for writing file content + * @return 0 success, return none zero(errno) if fail + */ + public int send(OutputStream out) throws IOException + { + FileInputStream fis; + int readBytes; + byte[] buff = new byte[256 * 1024]; + + fis = new FileInputStream(this.local_filename); + try + { + while ((readBytes=fis.read(buff)) >= 0) + { + if (readBytes == 0) + { + continue; + } + + out.write(buff, 0, readBytes); + } + } + finally + { + fis.close(); + } + + return 0; + } +}