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