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 18 package java.io; 19 20 import dalvik.system.CloseGuard; 21 import java.nio.NioUtils; 22 import java.nio.channels.FileChannel; 23 import libcore.io.IoBridge; 24 25 import static android.system.OsConstants.*; 26 27 /** 28 * An output stream that writes bytes to a file. If the output file exists, it 29 * can be replaced or appended to. If it does not exist, a new file will be 30 * created. 31 * <pre> {@code 32 * File file = ... 33 * OutputStream out = null; 34 * try { 35 * out = new BufferedOutputStream(new FileOutputStream(file)); 36 * ... 37 * } finally { 38 * if (out != null) { 39 * out.close(); 40 * } 41 * } 42 * }</pre> 43 * 44 * <p>This stream is <strong>not buffered</strong>. Most callers should wrap 45 * this stream with a {@link BufferedOutputStream}. 46 * 47 * <p>Use {@link FileWriter} to write characters, as opposed to bytes, to a file. 48 * 49 * @see BufferedOutputStream 50 * @see FileInputStream 51 */ 52 public class FileOutputStream extends OutputStream { 53 54 private FileDescriptor fd; 55 private final boolean shouldClose; 56 57 /** The unique file channel. Lazily initialized because it's rarely needed. */ 58 private FileChannel channel; 59 60 /** File access mode */ 61 private final int mode; 62 63 private final CloseGuard guard = CloseGuard.get(); 64 65 /** 66 * Constructs a new {@code FileOutputStream} that writes to {@code file}. The file will be 67 * truncated if it exists, and created if it doesn't exist. 68 * 69 * @throws FileNotFoundException if file cannot be opened for writing. 70 */ FileOutputStream(File file)71 public FileOutputStream(File file) throws FileNotFoundException { 72 this(file, false); 73 } 74 75 /** 76 * Constructs a new {@code FileOutputStream} that writes to {@code file}. 77 * If {@code append} is true and the file already exists, it will be appended to; otherwise 78 * it will be truncated. The file will be created if it does not exist. 79 * 80 * @throws FileNotFoundException if the file cannot be opened for writing. 81 */ FileOutputStream(File file, boolean append)82 public FileOutputStream(File file, boolean append) throws FileNotFoundException { 83 if (file == null) { 84 throw new NullPointerException("file == null"); 85 } 86 this.mode = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC); 87 this.fd = IoBridge.open(file.getPath(), mode); 88 this.shouldClose = true; 89 this.guard.open("close"); 90 } 91 92 /** 93 * Constructs a new {@code FileOutputStream} that writes to {@code fd}. 94 * 95 * @throws NullPointerException if {@code fd} is null. 96 */ FileOutputStream(FileDescriptor fd)97 public FileOutputStream(FileDescriptor fd) { 98 if (fd == null) { 99 throw new NullPointerException("fd == null"); 100 } 101 this.fd = fd; 102 this.shouldClose = false; 103 this.mode = O_WRONLY; 104 this.channel = NioUtils.newFileChannel(this, fd, mode); 105 // Note that we do not call guard.open here because the 106 // FileDescriptor is not owned by the stream. 107 } 108 109 /** 110 * Constructs a new {@code FileOutputStream} that writes to {@code path}. The file will be 111 * truncated if it exists, and created if it doesn't exist. 112 * 113 * @throws FileNotFoundException if file cannot be opened for writing. 114 */ FileOutputStream(String path)115 public FileOutputStream(String path) throws FileNotFoundException { 116 this(path, false); 117 } 118 119 /** 120 * Constructs a new {@code FileOutputStream} that writes to {@code path}. 121 * If {@code append} is true and the file already exists, it will be appended to; otherwise 122 * it will be truncated. The file will be created if it does not exist. 123 * 124 * @throws FileNotFoundException if the file cannot be opened for writing. 125 */ FileOutputStream(String path, boolean append)126 public FileOutputStream(String path, boolean append) throws FileNotFoundException { 127 this(new File(path), append); 128 } 129 130 @Override close()131 public void close() throws IOException { 132 guard.close(); 133 synchronized (this) { 134 if (channel != null) { 135 channel.close(); 136 } 137 if (shouldClose) { 138 IoBridge.closeAndSignalBlockedThreads(fd); 139 } else { 140 // An owned fd has been invalidated by IoUtils.close, but 141 // we need to explicitly stop using an unowned fd (http://b/4361076). 142 fd = new FileDescriptor(); 143 } 144 } 145 } 146 finalize()147 @Override protected void finalize() throws IOException { 148 try { 149 if (guard != null) { 150 guard.warnIfOpen(); 151 } 152 close(); 153 } finally { 154 try { 155 super.finalize(); 156 } catch (Throwable t) { 157 // for consistency with the RI, we must override Object.finalize() to 158 // remove the 'throws Throwable' clause. 159 throw new AssertionError(t); 160 } 161 } 162 } 163 164 /** 165 * Returns a write-only {@link FileChannel} that shares its position with 166 * this stream. 167 */ getChannel()168 public FileChannel getChannel() { 169 synchronized (this) { 170 if (channel == null) { 171 channel = NioUtils.newFileChannel(this, fd, mode); 172 } 173 return channel; 174 } 175 } 176 177 /** 178 * Returns the underlying file descriptor. 179 */ getFD()180 public final FileDescriptor getFD() throws IOException { 181 return fd; 182 } 183 184 @Override write(byte[] buffer, int byteOffset, int byteCount)185 public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { 186 IoBridge.write(fd, buffer, byteOffset, byteCount); 187 } 188 189 @Override write(int oneByte)190 public void write(int oneByte) throws IOException { 191 write(new byte[] { (byte) oneByte }, 0, 1); 192 } 193 } 194