    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.hayabusa.servlet.multipart;
    017
    018import java.io.FilterInputStream;
    019import java.io.IOException;
    020import javax.servlet.ServletInputStream;
    021
    022/**
    023 * ファイルアップロード時のマルチパート処理のファイル読取ストリームです。
    024 *
    025 * @og.group その他機能
    026 *
    027 * @version  4.0
    028 * @author   Kazuhiko Hasegawa
    029 * @since    JDK5.0,
    030 */
    031public class PartInputStream extends FilterInputStream {
    032        private final String boundary;
    033        private final byte [] buf = new byte[64*1024];  // 64k
    034        private int count;
    035        private int pos;
    036        private boolean eof;
    037
    038        /**
    039         * 読取ストリーム と区切り文字を指定してクラスを構築する コンストラクター
    040         *
    041         * @param       in                      ServletInputStreamオブジェクト
    042         * @param       boundary        境界文字
    043         * @throws IOException  上位の入出力エラー
    044         */
    045        PartInputStream( final ServletInputStream in, final String boundary) throws IOException {
    046                super(in);
    047                this.boundary = boundary;
    048        }
    049
    050        /**
    051         * データを埋めます。
    052         *
    053         * @throws IOException 入出力エラーが発生したとき
    054         */
    055        private void fill() throws IOException {
    056                if(eof) {
    057                        return;
    058                }
    059                // as long as we are not just starting up
    060                if(count > 0) {
    061                        // if the caller left the requisite amount spare in the buffer
    062                        if(count - pos == 2) {
    063                                // copy it back to the start of the buffer
    064                                System.arraycopy(buf, pos, buf, 0, count - pos);
    065                                count -= pos;
    066                                pos = 0;
    067                        } else {
    068                                // should never happen, but just in case
    069                                throw new IllegalStateException("fill() detected illegal buffer state");
    070                        }
    071                }
    072
    073                // try and fill the entire buffer, starting at count, line by line
    074                // but never read so close to the end that we might split a boundary
    075                int read;
    076                int maxRead = buf.length - boundary.length();
    077                while (count < maxRead) {
    078                        // read a line
    079                        read = ((ServletInputStream)in).readLine(buf, count, buf.length - count);
    080                        // check for eof and boundary
    081                        if(read == -1) {
    082                                throw new IOException("unexpected end of part");
    083                        } else {
    084                                if(read >= boundary.length()) {
    085                                        eof = true;
    086                                        for(int i=0; i < boundary.length(); i++) {
    087                                                if(boundary.charAt(i) != buf[count + i]) {
    088                                                        // Not the boundary!
    089                                                        eof = false;
    090                                                        break;
    091                                                }
    092                                        }
    093                                        if(eof) {
    094                                                break;
    095                                        }
    096                                }
    097                        }
    098                        // success
    099                        count += read;
    100                }
    101        }
    102
    103        /**
    104         * データを読み込みます。
    105         *
    106         * @return      読み取られたデータ
    107         * @throws IOException 入出力エラーが発生したとき
    108         */
    109        @Override
    110        public int read() throws IOException {
    111                if(count - pos <= 2) {
    112                        fill();
    113                        if(count - pos <= 2) {
    114                                return -1;
    115                        }
    116                }
    117                return buf[pos++] & 0xff;
    118        }
    119
    120        /**
    121         * データを読み込みます。
    122         *
    123         * @param       bt      バイト配列
    124         *
    125         * @return      読み取られたデータ
    126         * @throws IOException 入出力エラーが発生したとき
    127         */
    128        @Override
    129        public int read( final byte[] bt ) throws IOException {
    130                return read(bt, 0, bt.length);
    131        }
    132
    133        /**
    134         * データを読み込みます。
    135         *
    136         * @param       bt      バイト配列
    137         * @param       off     開始バイト数
    138         * @param       len     読み取りバイト数
    139         *
    140         * @return      読み取られたデータ
    141         * @throws IOException 入出力エラーが発生したとき
    142         */
    143        @Override
    144        public int read( final byte[] bt, final int off, final int len ) throws IOException {
    145                int total = 0;
    146                if(len == 0) {
    147                        return 0;
    148                }
    149
    150                int avail = count - pos - 2;
    151                if(avail <= 0) {
    152                        fill();
    153                        avail = count - pos - 2;
    154                        if(avail <= 0) {
    155                                return -1;
    156                        }
    157                }
    158                int copy = Math.min(len, avail);
    159                System.arraycopy(buf, pos, bt, off, copy);
    160                pos += copy;
    161                total += copy;
    162
    163                while (total < len) {
    164                        fill();
    165                        avail = count - pos - 2;
    166                        if(avail <= 0) {
    167                                return total;
    168                        }
    169                        copy = Math.min(len - total, avail);
    170                        System.arraycopy(buf, pos, bt, off + total, copy);
    171                        pos += copy;
    172                        total += copy;
    173                }
    174                return total;
    175        }
    176
    177        /**
    178         * 利用可能かどうかを返します。
    179         *
    180         * @return      利用可能かどうか
    181         * @throws IOException 入出力エラーが発生したとき
    182         */
    183        @Override
    184        public int available() throws IOException {
    185                int avail = (count - pos - 2) + in.available();
    186                // Never return a negative value
    187                return (avail < 0 ? 0 : avail);
    188        }
    189
    190        /**
    191         * 接続を閉じます。
    192         *
    193         * @throws IOException 入出力エラーが発生したとき
    194         */
    195        @Override
    196        public void close() throws IOException {
    197                if(!eof) {
    198                        int size = read(buf, 0, buf.length);
    199                        while( size != -1) {
    200                                size = read(buf, 0, buf.length);
    201                        }
    202                }
    203        }
    204}