使用由 HTML 代码制作的 Web 表单将文件从 Web 浏览器上传到服务器是很简单的。这通常称为基于表单的文件上传,通常适用于基于 Web 的应用程序。独立或基于命令行的应用程序——有时需要将文件上传到 Web 服务器,但不能使用 HTML 格式的上传表单?那么,本文将通过实现以下解决方案来回答这个问题:
- 服务器:使用 Java servlet 接收从客户端上传的文件,并将其保存到服务器磁盘上的文件中。
- 客户端:是一个基于命令行的程序,它将HTTP POST 请求连同文件的二进制数据一起发送到 servlet。
我们可以将此方法称为基于非表单的文件上传。请记住,这不是 FTP 上传,因为它仍然使用 HTTP 协议来传输文件。
让我们来看看每个部分是如何实现的。
编码文件上传 Servlet
编写一个 Java servlet,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package net.codejava.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* This servlet demonstrates how to receive file uploaded from the client
* without using third-party upload library such as Commons File Upload.
* @author www.codejava.net
*/
public class ReceiveFileServlet extends HttpServlet {
static final String SAVE_DIR = "E:/Test/Upload/" ;
static final int BUFFER_SIZE = 4096 ;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// Gets file name for HTTP header
String fileName = request.getHeader( "fileName" );
File saveFile = new File(SAVE_DIR + fileName);
// prints out all header values
System.out.println( "===== Begin headers =====" );
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String headerName = names.nextElement();
System.out.println(headerName + " = " + request.getHeader(headerName));
}
System.out.println( "===== End headers =====\n" );
// opens input stream of the request for reading data
InputStream inputStream = request.getInputStream();
// opens an output stream for writing file
FileOutputStream outputStream = new FileOutputStream(saveFile);
byte [] buffer = new byte [BUFFER_SIZE];
int bytesRead = - 1 ;
System.out.println( "Receiving data..." );
while ((bytesRead = inputStream.read(buffer)) != - 1 ) {
outputStream.write(buffer, 0 , bytesRead);
}
System.out.println( "Data received." );
outputStream.close();
inputStream.close();
System.out.println( "File written to: " + saveFile.getAbsolutePath());
// sends response to client
response.getWriter().print( "UPLOAD DONE" );
}
}
|
此 servlet 在其doPost()中处理 HTTP POST 请求方法。这段代码中有一些重要的点:
- 上传文件的名称是从名为“ fileName ”的 HTTP 标头中检索的——该标头由客户端设置。
- 我们创建了一个saveFile对象,以便将上传文件保存到磁盘中。它的路径由SAVE_DIR常量和fileName头构成。FileOutputStream对象包装此saveFile以将字节数组写入文件。
- 最重要的一点是从HttpServletRequest打开InputStream以读取从客户端传输的字节。
- 我们使用 while 循环从请求的输入流中重复读取字节数组并将它们写入saveFile的输出流。这是微不足道的。
- 最后,始终关闭打开的输入流和输出流,然后向客户端发送响应(在这种情况下,是一条简单的文本消息“UPLOAD DONE”)。
现在让我们看看客户端是如何实现的。
编写文件上传客户端程序
以下是客户端程序的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* This program demonstrates how to upload files to a web server
* using HTTP POST request without any HTML form.
* @author www.codejava.net
*
*/
public class NonFormFileUploader {
static final String UPLOAD_URL = "http://localhost:8080/CodeWeb/ReceiveFileServlet" ;
static final int BUFFER_SIZE = 4096 ;
public static void main(String[] args) throws IOException {
// takes file path from first program's argument
String filePath = args[ 0 ];
File uploadFile = new File(filePath);
System.out.println( "File to upload: " + filePath);
// creates a HTTP connection
URL url = new URL(UPLOAD_URL);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setUseCaches( false );
httpConn.setDoOutput( true );
httpConn.setRequestMethod( "POST" );
// sets file name as a HTTP header
httpConn.setRequestProperty( "fileName" , uploadFile.getName());
// opens output stream of the HTTP connection for writing data
OutputStream outputStream = httpConn.getOutputStream();
// Opens input stream of the file for reading data
FileInputStream inputStream = new FileInputStream(uploadFile);
byte [] buffer = new byte [BUFFER_SIZE];
int bytesRead = - 1 ;
System.out.println( "Start writing data..." );
while ((bytesRead = inputStream.read(buffer)) != - 1 ) {
outputStream.write(buffer, 0 , bytesRead);
}
System.out.println( "Data was written." );
outputStream.close();
inputStream.close();
// always check HTTP response code from server
int responseCode = httpConn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// reads server's response
BufferedReader reader = new BufferedReader( new InputStreamReader(
httpConn.getInputStream()));
String response = reader.readLine();
System.out.println( "Server's response: " + response);
} else {
System.out.println( "Server returned non-OK code: " + responseCode);
}
}
}
|
这里的重点是我们使用HttpURLConnection对象在 servlet 的 URL(由常量UPLOAD_URL标识)处向服务器发送 HTTP POST 请求,并打开此连接的OutputStream以写入上传文件的字节数组。该文件的路径作为程序的第一个参数传递。
与 servlet 类似,我们使用 while 循环从文件的输入流/请求的输出流读取/写入字节数组。
最后,我们在使用请求的输入流关闭两个输入/输出流后从服务器读取响应。
测试文件上传应用程序
在 Tomcat 等 servlet 容器上部署ReceiveFileServlet并启动服务器。然后通过执行以下命令运行客户端程序:
java NonFormFileUploader e:\Test\RecordAudio.wav
这里我们传递一个要上传的 WAV 音频文件。服务器将产生一些输出,如下图所示:
从这个日志中,我们可以检查从客户端发送的所有 HTTP 标头的值。几乎所有的值都是由 Java 自动设置的,除了文件名是从客户端手动设置的。
这是客户端程序的输出:
笔记:
- 服务器和客户端都没有处理多部分请求(这是基于表单的上传的标准)。请注意,请求的内容类型设置为 application/x-www-form-urlencoded 而不是 multipart/form-data。
- 因此客户端程序不能应用于接受多部分请求的服务器。服务器端必须像 ReceiveFileServlet那样实现。