001/* 002 * Copyright 2002-2020 the original author or authors. 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 * https://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, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core.io; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.OutputStream; 022import java.net.MalformedURLException; 023import java.net.URL; 024import java.nio.channels.FileChannel; 025import java.nio.channels.WritableByteChannel; 026import java.nio.file.Files; 027import java.nio.file.StandardOpenOption; 028 029import org.springframework.lang.Nullable; 030import org.springframework.util.ResourceUtils; 031 032/** 033 * Subclass of {@link UrlResource} which assumes file resolution, to the degree 034 * of implementing the {@link WritableResource} interface for it. This resource 035 * variant also caches resolved {@link File} handles from {@link #getFile()}. 036 * 037 * <p>This is the class resolved by {@link DefaultResourceLoader} for a "file:..." 038 * URL location, allowing a downcast to {@link WritableResource} for it. 039 * 040 * <p>Alternatively, for direct construction from a {@link java.io.File} handle 041 * or NIO {@link java.nio.file.Path}, consider using {@link FileSystemResource}. 042 * 043 * @author Juergen Hoeller 044 * @since 5.0.2 045 */ 046public class FileUrlResource extends UrlResource implements WritableResource { 047 048 @Nullable 049 private volatile File file; 050 051 052 /** 053 * Create a new {@code FileUrlResource} based on the given URL object. 054 * <p>Note that this does not enforce "file" as URL protocol. If a protocol 055 * is known to be resolvable to a file, it is acceptable for this purpose. 056 * @param url a URL 057 * @see ResourceUtils#isFileURL(URL) 058 * @see #getFile() 059 */ 060 public FileUrlResource(URL url) { 061 super(url); 062 } 063 064 /** 065 * Create a new {@code FileUrlResource} based on the given file location, 066 * using the URL protocol "file". 067 * <p>The given parts will automatically get encoded if necessary. 068 * @param location the location (i.e. the file path within that protocol) 069 * @throws MalformedURLException if the given URL specification is not valid 070 * @see UrlResource#UrlResource(String, String) 071 * @see ResourceUtils#URL_PROTOCOL_FILE 072 */ 073 public FileUrlResource(String location) throws MalformedURLException { 074 super(ResourceUtils.URL_PROTOCOL_FILE, location); 075 } 076 077 078 @Override 079 public File getFile() throws IOException { 080 File file = this.file; 081 if (file != null) { 082 return file; 083 } 084 file = super.getFile(); 085 this.file = file; 086 return file; 087 } 088 089 @Override 090 public boolean isWritable() { 091 try { 092 File file = getFile(); 093 return (file.canWrite() && !file.isDirectory()); 094 } 095 catch (IOException ex) { 096 return false; 097 } 098 } 099 100 @Override 101 public OutputStream getOutputStream() throws IOException { 102 return Files.newOutputStream(getFile().toPath()); 103 } 104 105 @Override 106 public WritableByteChannel writableChannel() throws IOException { 107 return FileChannel.open(getFile().toPath(), StandardOpenOption.WRITE); 108 } 109 110 @Override 111 public Resource createRelative(String relativePath) throws MalformedURLException { 112 return new FileUrlResource(createRelativeURL(relativePath)); 113 } 114 115}