My customer wants to upload files by https based on our current upload framework which depends on Apache VFS. Apache VFS only supports "READ" on HTTP/HTTPS. I generats the plugin to extend VFS to support "WRITE" on HTTP/HTTPS.
The idea of Apache VFS is using the same API for accessing various different file systems, such like FTP, SFTP, HTTP, ZIP...and so on.
To make HTTP/HTTPS writable, I added 3 classes.
- WritableHttpFileObject
- WritableHttpProvider
- WritableHttpFileSystem
WritableHttpFileObject
import java.io.FileOutputStream;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs.FileName;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSelector;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.Selectors;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import org.apache.commons.vfs.provider.http.HttpFileObject;
import org.apache.commons.vfs.provider.http.HttpFileSystem;
/**
* A writable file on an Http/Https service.
*
* The only really useful method is copyFrom() which causes the source file
* to actually be POSTed to the server with the specified filename.
*/
public class WritableHttpFileObject extends HttpFileObject {
private static final Log LOG = LogFactory.getLog(WritableHttpFileObject.class);
private FileOutputStream outputStream;
private String targetURL;
private final WritableHttpFileSystem fileSystem;
public WritableHttpFileObject(FileName fileName, WritableHttpFileSystem fileSystem) {
super(fileName, fileSystem);
this.fileSystem = fileSystem;
}
public boolean isWriteable() throws FileSystemException {
return true;
}
public void createFile() throws FileSystemException {
// Nothing to do
}
public void copyFrom(FileObject fileObject, FileSelector selector)
throws FileSystemException {
if (!(fileObject.getType().equals(FileType.FILE) && selector.equals(Selectors.SELECT_SELF))) {
throw new FileSystemException("Can only copy one files (one at a time)");
}
PostMethod filePost = new PostMethod(getURL().toString());
StandardFileSystemManager fsManager = new StandardFileSystemManager();
try {
LOG.info("Uploading " + fileObject.getName() + " to " + targetURL);
byte[] fileContent = IOUtils.toByteArray(fileObject.getContent().getInputStream());
ByteArrayPartSource byteArrayPartSource = new ByteArrayPartSource(fileObject.getName().getBaseName(), fileContent);
Part[] parts = {
new FilePart(fileObject.getName().getBaseName(), byteArrayPartSource)
};
filePost.setRequestEntity(
new MultipartRequestEntity(parts, filePost.getParams())
);
HttpClient client = fileSystem.getClient();
client.getHttpConnectionManager().
getParams().setConnectionTimeout(5000);
int status = client.executeMethod(filePost);
if (status == HttpStatus.SC_OK) {
appendMessage(
"Upload complete, response=" + filePost.getResponseBodyAsString()
);
} else {
appendMessage(
"Upload failed, response=" + HttpStatus.getStatusText(status)
);
}
} catch (Exception ex) {
appendMessage("ERROR: " + ex.getClass().getName() + " " + ex.getMessage());
ex.printStackTrace();
} finally {
filePost.releaseConnection();
}
}
private void appendMessage(final String msg) {
LOG.info(msg);
}
}
WritableHttpFileProvider
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.vfs.Capability;
import org.apache.commons.vfs.FileName;
import org.apache.commons.vfs.FileSystem;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.UserAuthenticationData;
import org.apache.commons.vfs.provider.GenericFileName;
import org.apache.commons.vfs.provider.http.HttpClientFactory;
import org.apache.commons.vfs.provider.http.HttpFileProvider;
import org.apache.commons.vfs.provider.http.HttpFileSystem;
import org.apache.commons.vfs.util.UserAuthenticatorUtils;
public class WritableHttpFileProvider extends HttpFileProvider {
final static Collection capabilities = Collections.unmodifiableCollection(Arrays.asList(new Capability[]
{
Capability.GET_TYPE,
Capability.READ_CONTENT,
Capability.WRITE_CONTENT,
Capability.URI,
Capability.GET_LAST_MODIFIED,
Capability.ATTRIBUTES,
Capability.RANDOM_ACCESS_READ
}));
/**
* Creates a {@link FileSystem}.
*/
protected FileSystem doCreateFileSystem(final FileName name, final FileSystemOptions fileSystemOptions)
throws FileSystemException
{
// Create the file system
final GenericFileName rootName = (GenericFileName) name;
UserAuthenticationData authData = null;
HttpClient httpClient;
try
{
authData = UserAuthenticatorUtils.authenticate(fileSystemOptions, AUTHENTICATOR_TYPES);
httpClient = HttpClientFactory.createConnection(
rootName.getScheme(),
rootName.getHostName(),
rootName.getPort(),
UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME, UserAuthenticatorUtils.toChar(rootName.getUserName()))),
UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD, UserAuthenticatorUtils.toChar(rootName.getPassword()))),
fileSystemOptions);
}
finally
{
UserAuthenticatorUtils.cleanup(authData);
}
return new WritableHttpFileSystem(rootName, httpClient, fileSystemOptions);
}
}
WritableHttpFileSystem
import java.util.Collection;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.vfs.FileName;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.provider.GenericFileName;
import org.apache.commons.vfs.provider.http.HttpFileSystem;
public class WritableHttpFileSystem extends HttpFileSystem {
protected WritableHttpFileSystem(GenericFileName rootName,
HttpClient client, FileSystemOptions fileSystemOptions) {
super(rootName, client, fileSystemOptions);
}
protected void addCapabilities(Collection caps)
{
caps.addAll(WritableHttpFileProvider.capabilities);
}
protected FileObject createFile(FileName name)
throws Exception
{
return new WritableHttpFileObject(name, this);
}
protected HttpClient getClient()
{
return super.getClient();
}
}
On the server side, you should have a servlet or controller to get the file from the request and save it as a file. To test it quickly, I create a jsp
<%@ page contentType="text/html;charset=utf-8"%>
<%@ page import="org.apache.commons.fileupload.DiskFileUpload"%>
<%@ page import="org.apache.commons.fileupload.FileItem"%>
<%@ page import="java.util.List"%>
<%@ page import="java.util.Iterator"%>
<%@ page import="java.io.File"%>
<%
System.out.println("Content Type ="+request.getContentType());
if (ServletFileUpload.isMultipartContent(request)){
// Parse the HTTP request...
ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());
List fileItemsList = servletFileUpload.parseRequest(request);
Iterator it = fileItemsList.iterator();
while (it.hasNext()){
FileItem fileItem = (FileItem)it.next();
if (fileItem.isFormField()){
System.out.println("Field ="+fileItem.getFieldName());
}
else{
System.out.println("\nNAME: "+fileItem.getName());
System.out.println("SIZE: "+fileItem.getSize());
//System.out.println(fi.getOutputStream().toString());
File fNew= new File(request.getPathInfo(), fileItem.getName());
System.out.println("\n"+fNew.getAbsolutePath());
fileItem.write(fNew);
}
}
} else{
System.out.println("the servlet request is not multipart content");
}
%>
The class to test WritableHttpFileObject
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.Selectors;
import org.apache.commons.vfs.cache.DefaultFilesCache;
import org.apache.commons.vfs.impl.DefaultFileSystemManager;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import org.apache.commons.vfs.provider.http.HttpFileProvider;
import org.apache.commons.vfs.provider.http.HttpFileSystemConfigBuilder;
import org.apache.commons.vfs.provider.local.DefaultLocalFileProvider;
import org.junit.Test;
public class WritableHttpFileObjectTest {
@Test
public void testCopyFromFileObjectFileSelector() {
String filePath = "C:/temp/test.txt";
String uri = "http://localhost:8080/cmserver/test.jsp";
System.out.println("File Path: " + filePath);
DefaultFileSystemManager manager = getDefaultFileSystemManager();
try {
FileObject localFileObject = manager.resolveFile(filePath);
FileObject fileObject = manager.resolveFile(uri, null);
fileObject.copyFrom(localFileObject, Selectors.SELECT_SELF);
} catch (Exception e) {
e.printStackTrace();
}
finally {
manager.close();
}
}
private DefaultFileSystemManager getDefaultFileSystemManager() {
DefaultFileSystemManager mgr = new DefaultFileSystemManager();
WritableHttpFileProvider writableHttpFileProvider = new WritableHttpFileProvider();
DefaultLocalFileProvider localFileProvider = new DefaultLocalFileProvider();
try {
mgr.addProvider("http", writableHttpFileProvider);
mgr.addProvider("file", localFileProvider);
mgr.setFilesCache(new DefaultFilesCache());
mgr.init();
} catch (FileSystemException e) {
e.printStackTrace();
}
return mgr;
}
}
The simplest method is to use the static VFS.getManager() method, which returns the default Commons VFS implementation.
This method will also automatically scan the classpath for a /META-INF/vfs-providers.xml file (also in jar files). If such a file is found Commons VFS uses it in addition to the default providers.xml (it is in org.apache.commons.vfs.iml in the vfs jar). This allows you to start using a new filesystem by simply drop its implementation into the classpath. The configuration file format is described below.
Notice: Currently it is not allowed to override a already configured filesystem. Commons VFS throws an exception if there is already a filesystem for a scheme.
No comments:
Post a Comment