1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.fileupload.util;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23
24 import org.apache.commons.fileupload.InvalidFileNameException;
25 import org.apache.commons.io.IOUtils;
26
27 /**
28 * Utility class for working with streams.
29 */
30 public final class Streams {
31
32 /**
33 * Private constructor, to prevent instantiation.
34 * This class has only static methods.
35 */
36 private Streams() {
37 // Does nothing
38 }
39
40 /**
41 * Default buffer size for use in
42 * {@link #copy(InputStream, OutputStream, boolean)}.
43 */
44 private static final int DEFAULT_BUFFER_SIZE = 8192;
45
46 /**
47 * Copies the contents of the given {@link InputStream}
48 * to the given {@link OutputStream}. Shortcut for
49 * <pre>
50 * copy(pInputStream, pOutputStream, new byte[8192]);
51 * </pre>
52 *
53 * @param inputStream The input stream, which is being read.
54 * It is guaranteed, that {@link InputStream#close()} is called
55 * on the stream.
56 * @param outputStream The output stream, to which data should
57 * be written. May be null, in which case the input streams
58 * contents are simply discarded.
59 * @param closeOutputStream True guarantees, that {@link OutputStream#close()}
60 * is called on the stream. False indicates, that only
61 * {@link OutputStream#flush()} should be called finally.
62 *
63 * @return Number of bytes, which have been copied.
64 * @throws IOException An I/O error occurred.
65 */
66 public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream)
67 throws IOException {
68 return copy(inputStream, outputStream, closeOutputStream, new byte[DEFAULT_BUFFER_SIZE]);
69 }
70
71 /**
72 * Copies the contents of the given {@link InputStream}
73 * to the given {@link OutputStream}.
74 *
75 * @param inputStream The input stream, which is being read.
76 * It is guaranteed, that {@link InputStream#close()} is called
77 * on the stream.
78 * @param outputStream The output stream, to which data should
79 * be written. May be null, in which case the input streams
80 * contents are simply discarded.
81 * @param closeOutputStream True guarantees, that {@link OutputStream#close()}
82 * is called on the stream. False indicates, that only
83 * {@link OutputStream#flush()} should be called finally.
84 * @param buffer Temporary buffer, which is to be used for
85 * copying data.
86 * @return Number of bytes, which have been copied.
87 * @throws IOException An I/O error occurred.
88 */
89 public static long copy(InputStream inputStream,
90 OutputStream outputStream, boolean closeOutputStream,
91 byte[] buffer)
92 throws IOException {
93 OutputStream out = outputStream;
94 InputStream in = inputStream;
95 try {
96 long total = 0;
97 for (;;) {
98 int res = in.read(buffer);
99 if (res == -1) {
100 break;
101 }
102 if (res > 0) {
103 total += res;
104 if (out != null) {
105 out.write(buffer, 0, res);
106 }
107 }
108 }
109 if (out != null) {
110 if (closeOutputStream) {
111 out.close();
112 } else {
113 out.flush();
114 }
115 out = null;
116 }
117 in.close();
118 in = null;
119 return total;
120 } finally {
121 IOUtils.closeQuietly(in);
122 if (closeOutputStream) {
123 IOUtils.closeQuietly(out);
124 }
125 }
126 }
127
128 /**
129 * This convenience method allows to read a
130 * {@link org.apache.commons.fileupload.FileItemStream}'s
131 * content into a string. The platform's default character encoding
132 * is used for converting bytes into characters.
133 *
134 * @param inputStream The input stream to read.
135 * @see #asString(InputStream, String)
136 * @return The streams contents, as a string.
137 * @throws IOException An I/O error occurred.
138 */
139 public static String asString(InputStream inputStream) throws IOException {
140 ByteArrayOutputStream baos = new ByteArrayOutputStream();
141 copy(inputStream, baos, true);
142 return baos.toString();
143 }
144
145 /**
146 * This convenience method allows to read a
147 * {@link org.apache.commons.fileupload.FileItemStream}'s
148 * content into a string, using the given character encoding.
149 *
150 * @param inputStream The input stream to read.
151 * @param encoding The character encoding, typically "UTF-8".
152 * @see #asString(InputStream)
153 * @return The streams contents, as a string.
154 * @throws IOException An I/O error occurred.
155 */
156 public static String asString(InputStream inputStream, String encoding) throws IOException {
157 ByteArrayOutputStream baos = new ByteArrayOutputStream();
158 copy(inputStream, baos, true);
159 return baos.toString(encoding);
160 }
161
162 /**
163 * Checks, whether the given file name is valid in the sense,
164 * that it doesn't contain any NUL characters. If the file name
165 * is valid, it will be returned without any modifications. Otherwise,
166 * an {@link InvalidFileNameException} is raised.
167 *
168 * @param fileName The file name to check
169 * @return Unmodified file name, if valid.
170 * @throws InvalidFileNameException The file name was found to be invalid.
171 */
172 public static String checkFileName(String fileName) {
173 if (fileName != null && fileName.indexOf('\u0000') != -1) {
174 // pFileName.replace("\u0000", "\\0")
175 final StringBuilder sb = new StringBuilder();
176 for (int i = 0; i < fileName.length(); i++) {
177 char c = fileName.charAt(i);
178 switch (c) {
179 case 0:
180 sb.append("\\0");
181 break;
182 default:
183 sb.append(c);
184 break;
185 }
186 }
187 throw new InvalidFileNameException(fileName,
188 "Invalid file name: " + sb);
189 }
190 return fileName;
191 }
192
193 }