public int setMetadata(String groupName, String remoteFileName, Map<String, String> meta, byte opFlag)
            throws FdfsException {
        checkBefore(groupName, remoteFileName);
        byte[] sizeBytes = new byte[2 * Constants.FDFS_PROTO_PKG_LEN_SIZE];
        // fileNameLength
        byte[] remoteFileNameByteArr = Strings.getBytes(remoteFileName, getFdfsClientConfig().getCharset());
        byte[] remoteFileNameByteArrLengthByteArr = new byte[8];
        Bytes.long2bytes(remoteFileNameByteArr.length, remoteFileNameByteArrLengthByteArr);
        System.arraycopy(remoteFileNameByteArrLengthByteArr, 0, sizeBytes, 0,
            remoteFileNameByteArrLengthByteArr.length);
        // metaData
        byte[] metaDataByteArr = new byte[0];
        if (meta != null && meta.size() > 0) {
            StringBuilder sb = new StringBuilder();
            int i = 0;
            for (Map.Entry<String, String> entry : meta.entrySet()) {
                if (i != 0) {
                    sb.append(Constants.FDFS_RECORD_SEPERATOR);
                }
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key).append(Constants.FDFS_FIELD_SEPERATOR).append(value);
                i++;
            }
            metaDataByteArr = Strings.getBytes(sb.toString(), getFdfsClientConfig().getCharset());
        }
        byte[] metaDataLengthByteArr = new byte[8];
        Bytes.long2bytes(metaDataByteArr.length, metaDataLengthByteArr);
        System.arraycopy(metaDataLengthByteArr, 0, sizeBytes, 8, metaDataLengthByteArr.length);
        // groupName
        byte[] groupNameByteArr = Strings.getBytes(groupName, getFdfsClientConfig().getCharset());
        groupNameByteArr = Bytes.wrap(groupNameByteArr, Constants.FDFS_GROUP_NAME_MAX_LEN);

        byte cmd = Constants.STORAGE_PROTO_CMD_SET_METADATA;
        // +1 ==> opFlag , one byte
        long bodyLength =
                sizeBytes.length + 1 + groupNameByteArr.length + remoteFileNameByteArr.length
                        + metaDataByteArr.length;

        byte[] header = Messages.createHeader(bodyLength, cmd, (byte) 0);
        int wholeLength = (int) (bodyLength + header.length);
        WriteByteArrayFragment headerBodyData = new WriteByteArrayFragment(wholeLength);
        // fill header
        headerBodyData.writeBytes(header);
        // fill sizeBytes
        headerBodyData.writeBytes(sizeBytes);
        // fill opFlag
        headerBodyData.writeByte(opFlag);
        // fill groupName
        headerBodyData.writeBytes(groupNameByteArr);
        // fill remoteFileName
        headerBodyData.writeBytes(remoteFileNameByteArr);
        // fill meta
        if (metaDataByteArr.length > 0) {
            headerBodyData.writeBytes(metaDataByteArr);
        }
        // send
        ResponseBody responseBody = null;
        Connection con = getUpdatableConnection(groupName, remoteFileName);
        try {
            con.writeBytes(headerBodyData.getData());
            // recv
            InputStream is = con.getInputStream();
            responseBody = Messages.readResponse(is, 0);
        } catch (Exception e) {
            String msg = "groupName=[{}], remoteFileName=[{}], meta=[{}], opFlag=[{}]";
            msg = Strings.format(msg, groupName, remoteFileName, meta, opFlag);
            throw new FdfsException(msg + e.getMessage(), e);
        } finally {
            con.close();
        }
        return responseBody.getErrorNo();
    }