这是我原来的答案:
对不起,如果我给你一些错误的信息,很长时间没有使用 FOP。
如Apache XML 图形图像加载框架文档中所述,为了预加载图像,可能在您的代码的某些部分中,您有如下内容:
ImageSessionContext sessionContext = new DefaultImageSessionContext(
getImageManager().getImageContext(), null);
ImageInfo info = getImageManager().getImageInfo(url.toString(), sessionContext);
如果您想支持重定向,正如您建议的那样,您可以提供您的自定义实现ImageSessionContext
.
查看 的源代码,可能最好的方法是创建一个覆盖该方法DefaultImageSessionContext
的新类。resolveURI
请考虑以下基于 的代码,HttpURLConnection
它应该根据需要遵循重定向:
public class HttpRedirectsAwareImageSessionContext extends DefaultImageSessionContext {
@Override
protected Source resolveURI(String uri) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(uri);
urlConnection = (HttpURLConnection) url.openConnection();
// Just read the InputStream, to a byte[] for instance. As in the example
// you can use the convenient methods of Apache Commons IOUtils for the task
// as well. See: https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/IOUtils.html#toBufferedInputStream-java.io.InputStream-
InputStream in = IOUtils.toBufferedInputStream(urlConnection.getInputStream());
return new StreamSource(in, url.toExternalForm());
} catch (MalformedURLException e) {
// Legacy DefaultImageSessionContext code
File f = new File(baseDir, uri);
if (f.isFile()) {
return new StreamSource(f);
} else {
return null;
}
} catch (IOException ioe) {
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
}
然后,改用新创建的ImageSessionContext
:
ImageSessionContext sessionContext = new HttpRedirectsAwareImageSessionContext(
getImageManager().getImageContext(), null);
ImageInfo info = getImageManager().getImageInfo(url.toString(), sessionContext);
但恐怕我没有完全理解您的要求。
例如,如您所见,在FOPUserAgent
源代码中,FOP 提供了自己的底层ImageSessionContext
实现:
private final ImageSessionContext imageSessionContext;
/**
* Main constructor. <b>This constructor should not be called directly. Please use the
* methods from FopFactory to construct FOUserAgent instances!</b>
* @param factory the factory that provides environment-level information
* @param resourceResolver the resolver used to acquire resources
* @see org.apache.fop.apps.FopFactory
*/
FOUserAgent(final FopFactory factory, InternalResourceResolver resourceResolver) {
this.factory = factory;
this.resourceResolver = resourceResolver;
setTargetResolution(factory.getTargetResolution());
setAccessibility(factory.isAccessibilityEnabled());
setKeepEmptyTags(factory.isKeepEmptyTags());
imageSessionContext = new AbstractImageSessionContext(factory.getFallbackResolver()) {
public ImageContext getParentContext() {
return factory;
}
public float getTargetResolution() {
return FOUserAgent.this.getTargetResolution();
}
public Source resolveURI(String uri) {
return FOUserAgent.this.resolveURI(uri);
}
};
}
/**
* Attempts to resolve the given URI.
* Will use the configured resolver and if not successful fall back
* to the default resolver.
* @param uri URI to access
* @return A {@link javax.xml.transform.Source} object, or null if the URI
* cannot be resolved.
*/
public StreamSource resolveURI(String uri) {
// TODO: What do we want to do when resources aren't found??? We also need to remove this
// method entirely
try {
// Have to do this so we can resolve data URIs
StreamSource src = new StreamSource(resourceResolver.getResource(uri));
src.setSystemId(getResourceResolver().getBaseURI().toASCIIString());
return src;
} catch (URISyntaxException use) {
return null;
} catch (IOException ioe) {
return null;
}
}
此实现依赖于类InternalResourceResolver
,而后者又依赖于ResourceResolver
XML 图形图像加载框架提供的抽象。
为了解决您的问题,您可以ResourceResolver
在初始化 FOP 时通过FopFactoryBuilder
.
尽管已经过时,但 Apache FOP 文档在此处和本篇以及主要是其他文章中提供了一些有用的介绍。例如,根据上一个链接中提供的信息:
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = uri.toURL();
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
// Based on the above-mentioned code
HttpURLConnection urlConnection = null;
try {
URL url = uri.toURL();
// Please, see the companion comment and SO answer if you need
// http to https redirect
// https://stackoverflow.com/questions/1884230/httpurlconnection-doesnt-follow-redirect-from-http-to-https/26046079#26046079
urlConnection = (HttpURLConnection) url.openConnection();
// Just read the InputStream, to a byte[] for instance. As in the example
// you can use the convenient methods of Apache Commons IOUtils for the task
// as well. See: https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/IOUtils.html#toBufferedInputStream-java.io.InputStream-
InputStream in = IOUtils.toBufferedInputStream(urlConnection.getInputStream());
return new StreamSource(in, url.toExternalForm());
} catch (MalformedURLException e) {
// Legacy DefaultImageSessionContext code
File f = new File(baseDir, uri);
if (f.isFile()) {
return new StreamSource(f);
} else {
return null;
}
} catch (IOException ioe) {
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
};
//Setting up the FOP factory
FopFactoryBuilder builder = new FopFactoryBuilder(new File(".").toURI(), resolver);
fopFactory = builder.build();
请注意,这是一个非常简单的示例,实际ResourceResolver
由 FOP 构建的ResourceResolverFactory
非常复杂,但也许可以为您提供一些关于如何实现自己的想法:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.apps.io;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.xmlgraphics.io.Resource;
import org.apache.xmlgraphics.io.ResourceResolver;
import org.apache.xmlgraphics.io.TempResourceResolver;
import org.apache.xmlgraphics.io.TempResourceURIGenerator;
/**
* A factory class for {@link ResourceResolver}s.
*/
public final class ResourceResolverFactory {
private ResourceResolverFactory() {
}
/**
* Returns the default resource resolver, this is most basic resolver which can be used when
* no there are no I/O or file access restrictions.
*
* @return the default resource resolver
*/
public static ResourceResolver createDefaultResourceResolver() {
return DefaultResourceResolver.INSTANCE;
}
/**
* A helper merthod that creates an internal resource resolver using the default resover:
* {@link ResourceResolverFactory#createDefaultResourceResolver()}.
*
* @param baseURI the base URI from which to resolve URIs
* @return the default internal resource resolver
*/
public static InternalResourceResolver createDefaultInternalResourceResolver(URI baseURI) {
return new InternalResourceResolver(baseURI, createDefaultResourceResolver());
}
/**
* Creates an interal resource resolver given a base URI and a resource resolver.
*
* @param baseURI the base URI from which to resolve URIs
* @param resolver the resource resolver
* @return the internal resource resolver
*/
public static InternalResourceResolver createInternalResourceResolver(URI baseURI,
ResourceResolver resolver) {
return new InternalResourceResolver(baseURI, resolver);
}
/**
* Creates a temporary-resource-scheme aware resource resolver. Temporary resource URIs are
* created by {@link TempResourceURIGenerator}.
*
* @param tempResourceResolver the temporary-resource-scheme resolver to use
* @param defaultResourceResolver the default resource resolver to use
* @return the ressource resolver
*/
public static ResourceResolver createTempAwareResourceResolver(
TempResourceResolver tempResourceResolver,
ResourceResolver defaultResourceResolver) {
return new TempAwareResourceResolver(tempResourceResolver, defaultResourceResolver);
}
/**
* This creates the builder class for binding URI schemes to implementations of
* {@link ResourceResolver}. This allows users to define their own URI schemes such that they
* have finer control over the acquisition of resources.
*
* @param defaultResolver the default resource resolver that should be used in the event that
* none of the other registered resolvers match the scheme
* @return the scheme aware {@link ResourceResolver} builder
*/
public static SchemeAwareResourceResolverBuilder createSchemeAwareResourceResolverBuilder(
ResourceResolver defaultResolver) {
return new SchemeAwareResourceResolverBuilderImpl(defaultResolver);
}
private static final class DefaultResourceResolver implements ResourceResolver {
private static final ResourceResolver INSTANCE = new DefaultResourceResolver();
private final TempAwareResourceResolver delegate;
private DefaultResourceResolver() {
delegate = new TempAwareResourceResolver(new DefaultTempResourceResolver(),
new NormalResourceResolver());
}
/** {@inheritDoc} */
public Resource getResource(URI uri) throws IOException {
return delegate.getResource(uri);
}
/** {@inheritDoc} */
public OutputStream getOutputStream(URI uri) throws IOException {
return delegate.getOutputStream(uri);
}
}
private static final class TempAwareResourceResolver implements ResourceResolver {
private final TempResourceResolver tempResourceResolver;
private final ResourceResolver defaultResourceResolver;
public TempAwareResourceResolver(TempResourceResolver tempResourceHandler,
ResourceResolver defaultResourceResolver) {
this.tempResourceResolver = tempResourceHandler;
this.defaultResourceResolver = defaultResourceResolver;
}
private static boolean isTempURI(URI uri) {
return TempResourceURIGenerator.isTempURI(uri);
}
/** {@inheritDoc} */
public Resource getResource(URI uri) throws IOException {
if (isTempURI(uri)) {
return tempResourceResolver.getResource(uri.getPath());
} else {
return defaultResourceResolver.getResource(uri);
}
}
/** {@inheritDoc} */
public OutputStream getOutputStream(URI uri) throws IOException {
if (isTempURI(uri)) {
return tempResourceResolver.getOutputStream(uri.getPath());
} else {
return defaultResourceResolver.getOutputStream(uri);
}
}
}
private static class DefaultTempResourceResolver implements TempResourceResolver {
private final ConcurrentHashMap<String, File> tempFiles = new ConcurrentHashMap<String, File>();
private File getTempFile(String uri) throws IllegalStateException {
File tempFile = tempFiles.remove(uri);
if (tempFile == null) {
throw new IllegalStateException(uri + " was never created or has been deleted");
}
return tempFile;
}
private File createTempFile(String path) throws IOException {
File tempFile = File.createTempFile(path, ".fop.tmp");
File oldFile = tempFiles.put(path, tempFile);
if (oldFile != null) {
String errorMsg = oldFile.getAbsolutePath() + " has been already created for " + path;
boolean newTempDeleted = tempFile.delete();
if (!newTempDeleted) {
errorMsg += ". " + tempFile.getAbsolutePath() + " was not deleted.";
}
throw new IOException(errorMsg);
}
return tempFile;
}
/** {@inheritDoc} */
public Resource getResource(String id) throws IOException {
return new Resource(new FileDeletingInputStream(getTempFile(id)));
}
/** {@inheritDoc} */
public OutputStream getOutputStream(String id) throws IOException {
return new FileOutputStream(createTempFile(id));
}
}
private static class FileDeletingInputStream extends FilterInputStream {
private final File file;
protected FileDeletingInputStream(File file) throws MalformedURLException, IOException {
super(file.toURI().toURL().openStream());
this.file = file;
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
file.delete();
}
}
}
private static class NormalResourceResolver implements ResourceResolver {
public Resource getResource(URI uri) throws IOException {
return new Resource(uri.toURL().openStream());
}
public OutputStream getOutputStream(URI uri) throws IOException {
return new FileOutputStream(new File(uri));
}
}
private static final class SchemeAwareResourceResolver implements ResourceResolver {
private final Map<String, ResourceResolver> schemeHandlingResourceResolvers;
private final ResourceResolver defaultResolver;
private SchemeAwareResourceResolver(
Map<String, ResourceResolver> schemEHandlingResourceResolvers,
ResourceResolver defaultResolver) {
this.schemeHandlingResourceResolvers = schemEHandlingResourceResolvers;
this.defaultResolver = defaultResolver;
}
private ResourceResolver getResourceResolverForScheme(URI uri) {
String scheme = uri.getScheme();
if (schemeHandlingResourceResolvers.containsKey(scheme)) {
return schemeHandlingResourceResolvers.get(scheme);
} else {
return defaultResolver;
}
}
/** {@inheritDoc} */
public Resource getResource(URI uri) throws IOException {
return getResourceResolverForScheme(uri).getResource(uri);
}
/** {@inheritDoc} */
public OutputStream getOutputStream(URI uri) throws IOException {
return getResourceResolverForScheme(uri).getOutputStream(uri);
}
}
/**
* Implementations of this interface will be builders for {@link ResourceResolver}, they bind
* URI schemes to their respective resolver. This gives users more control over the mechanisms
* by which URIs are resolved.
* <p>
* Here is an example of how this could be used:
* </p>
* <p><code>
* SchemeAwareResourceResolverBuilder builder
* = ResourceResolverFactory.createSchemeAwareResourceResolverBuilder(defaultResolver);
* builder.registerResourceResolverForScheme("test", testResolver);
* builder.registerResourceResolverForScheme("anotherTest", test2Resolver);
* ResourceResolver resolver = builder.build();
* </code></p>
* This will result in all URIs for the form "test:///..." will be resolved using the
* <code>testResolver</code> object; URIs of the form "anotherTest:///..." will be resolved
* using <code>test2Resolver</code>; all other URIs will be resolved from the defaultResolver.
*/
public interface SchemeAwareResourceResolverBuilder {
/**
* Register a scheme with its respective {@link ResourceResolver}. This resolver will be
* used as the only resolver for the specified scheme.
*
* @param scheme the scheme to be used with the given resolver
* @param resourceResolver the resource resolver
*/
void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver);
/**
* Builds a {@link ResourceResolver} that will delegate to the respective resource resolver
* when a registered URI scheme is given
*
* @return a resolver that delegates to the appropriate scheme resolver
*/
ResourceResolver build();
}
private static final class CompletedSchemeAwareResourceResolverBuilder
implements SchemeAwareResourceResolverBuilder {
private static final SchemeAwareResourceResolverBuilder INSTANCE
= new CompletedSchemeAwareResourceResolverBuilder();
/** {@inheritDoc} */
public ResourceResolver build() {
throw new IllegalStateException("Resource resolver already built");
}
/** {@inheritDoc} */
public void registerResourceResolverForScheme(String scheme,
ResourceResolver resourceResolver) {
throw new IllegalStateException("Resource resolver already built");
}
}
private static final class ActiveSchemeAwareResourceResolverBuilder
implements SchemeAwareResourceResolverBuilder {
private final Map<String, ResourceResolver> schemeHandlingResourceResolvers
= new HashMap<String, ResourceResolver>();
private final ResourceResolver defaultResolver;
private ActiveSchemeAwareResourceResolverBuilder(ResourceResolver defaultResolver) {
this.defaultResolver = defaultResolver;
}
/** {@inheritDoc} */
public void registerResourceResolverForScheme(String scheme,
ResourceResolver resourceResolver) {
schemeHandlingResourceResolvers.put(scheme, resourceResolver);
}
/** {@inheritDoc} */
public ResourceResolver build() {
return new SchemeAwareResourceResolver(
Collections.unmodifiableMap(schemeHandlingResourceResolvers), defaultResolver);
}
}
private static final class SchemeAwareResourceResolverBuilderImpl
implements SchemeAwareResourceResolverBuilder {
private SchemeAwareResourceResolverBuilder delegate;
private SchemeAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) {
this.delegate = new ActiveSchemeAwareResourceResolverBuilder(defaultResolver);
}
/** {@inheritDoc} */
public void registerResourceResolverForScheme(String scheme,
ResourceResolver resourceResolver) {
delegate.registerResourceResolverForScheme(scheme, resourceResolver);
}
/** {@inheritDoc} */
public ResourceResolver build() {
ResourceResolver resourceResolver = delegate.build();
delegate = CompletedSchemeAwareResourceResolverBuilder.INSTANCE;
return resourceResolver;
}
}
}
注意NormalResourceResolver
和getResource
方法的实现。
提到的所有源代码都与可从此处下载的 FOP 2.6 版有关。