任意java程序都可以是servlet吗_C#从Java servlet中获取文件流实现任意文件下载
目的:
从C#创建的客户端中向Java创建的servlet提交请求(不知道这是否算作严格的CS架构),servet返回一个文件流( 任意格式的文件 ),然后通过C#获取该文件流并保存到本地,使得文件可以正常打开并完整无缺。
发现问题:
Java中的servlet代码如下:
package servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
@WebServlet(urlPatterns = "/update")
public class getUpdate extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
File file=new File("D:\\test.exe");
FileInputStream inputStream=new FileInputStream(file);
int i=inputStream.available();
byte[] data=new byte[i];
inputStream.read(data);
ServletOutputStream outputStream=resp.getOutputStream();
outputStream.write(data);
outputStream.flush();
outputStream.close();
inputStream.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
servlet的环境为本地,D盘下的test.exe是一个可执行的winform程序,用于文件的完整性测试。
将以上代码部署到tomcat后,直接在浏览器中输入“http://localhost:8080/update”可以下载到名为update的文件,给update添加.exe后缀后直接打开发现可以正常执行,这表明该servlet并没什么问题。
客户端的C#代码如下:
using System;
using System.IO;
using System.Net;
namespace ServiceTest
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:8080/update";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
WebResponse webResponse = webRequest.GetResponse();
Stream responseStream = webResponse.GetResponseStream();
FileStream fileStream = new FileStream("test.exe", FileMode.Create);
byte[] data = new byte[100000];
int count = responseStream.Read(data, 0, data.Length);
fileStream.Write(data, 0, count);
Console.WriteLine("OK");
fileStream.Close();
responseStream.Close();
}
}
}
刚开始我觉得test.exe文件不过只有15kb,这里给出的byte[100000]一定绰绰有余。所以当我执行程序时,发现控制台可以正常输出“OK”,但是在该项目目录下的test.exe文件却只有8kb:
test.exe属性
而且很明显,双击该文件无法执行:
test.exe执行结果
解决问题:
然后我用Java写了个测试代码:
package test;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public class Test2 {
public static void main(String[] args) {
String url = "http://localhost:8080/update";
try {
URLConnection connection = new URL(url).openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
InputStream inputStream = httpURLConnection.getInputStream();
int i = inputStream.available();
byte[] data = new byte[i];
inputStream.read(data);
File file = new File("D:\\test2.exe");
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(data);
System.out.println("finish");
outputStream.close();
inputStream.close();
httpURLConnection.disconnect();
((HttpURLConnection) connection).disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行后得到的test2.exe文件完全正常!
于是接下来分别对C#客户端代码和Java客户端代码进行调试:
C#调试
Java调试
从上面两张图可以看出data内容不尽相同,在C#中data[2]=144,但是在Java中data[2]=-112。原因是C#中的byte取值为0~255,而Java中的byte取值为-128~127。但这好像不是问题的关键,关键是在C#中的8192行以后,数据全部为空,到此截断。
C#调试
掐指一算,8192/1024=8。这时才明白new byte[100000]的写法是错误的,而且是及其荒谬可笑的不符合程序员思想的不能容忍的应当被枪毙的错误!
修正后的C#关键代码如下:
byte[] data = new byte[1024];
int count;
while ((count = responseStream.Read(data, 0, data.Length)) != 0)
{
fileStream.Write(data, 0, count);
}
同时意识到servlet中的写法也是不负责任的,当要传输的文件过大时,问题就会显现出来,因此修改后的servlet关键代码如下:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
File file=new File("D:\\test.exe");
FileInputStream inputStream=new FileInputStream(file);
ServletOutputStream outputStream=resp.getOutputStream();
byte[] data=new byte[1024];
int count;
while ((count=inputStream.read(data,0,data.length))!=-1){
outputStream.write(data,0,count);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
总结:
1.C#中的byte是无符号的,取值范围为0~255;而Java中的byte是有符号的,取值范围为-128~127。
2.C#中的byte[]最多只到8192,即8k,超出8192部分不会被存取。Java虽然没有此限制,但是直接new一个长度很长的数组去保存文件流信息仍是极其不推荐的。
3.C#中public abstract int Read(byte[] buffer, int offset, int count)函数当读取到文件结尾时返回值为0;而Java中public int read(byte b[], int off, int len)返回值为-1。
4.C#中的StreamReader和StreamWriter只适合读写字符串,而不能用于处理二进制文件。
任意java程序都可以是servlet吗_C#从Java servlet中获取文件流实现任意文件下载相关推荐
- java在电脑哪里_javapc软件_PC的JAVA程序都在哪里?
PC的JAVA程序都在哪里?答:java的桌面应用很少,除了小工具外,基本上很难说出有什么通用的桌面应用.这是因为:一.Java在桌面应用程序的界面开发上不占优势,不容易开发出比较美观的界面(除了SW ...
- java程序的装载与检查_浅谈Java类型装载、连接与初始化
类型装载.连接与初始化 Java虚拟机通过装载.连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用.其中装载就是把二进制形式的Java class文件读入Java虚拟机中去;连接 ...
- 编写高质量代码改善java程序的151个建议——[110-117]异常及Web项目中异常处理
编写高质量代码改善java程序的151个建议--[110-117]异常及Web项目中异常处理 原创地址:http://www.cnblogs.com/Alandre/(泥沙砖瓦浆木匠),需要转载的,保 ...
- java中有关文件流的操作
文件流: 顾名思义,程序和文件打交道. 此时我们谈及的文件,值得是纯文本文件(txt的,不要使用Word,Excel), 在字节流中,暂时不要使用中文. FileInputStream: 文件的字节输 ...
- java文件中获取创建日期_如何在Java中获取文件的上次修改日期
java文件中获取创建日期 Sometimes we need to get the file last modified date in Java, usually for listeners li ...
- JAVA中获取文件MD5值的四种方法
JAVA中获取文件MD5值的四种方法其实都很类似,因为核心都是通过JAVA自带的MessageDigest类来实现.获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过Messa ...
- java根据url获取文件流
最近的一个需求需要根据一个mp3的链接获取文件流上传到内部的存储平台, 记录一下获取文件流的代码吧. pom文件 <dependency><groupId>org.apache ...
- 每个java程序都至少有一个线程给主线程,java程序在主线程中判断各个子线程状态的操作,该如何解决...
java程序在主线程中判断各个子线程状态的操作 每个子线程在队列为空时会wait等待其他线程添加新url到队列,到最后所有子线程都取不到url时也会都wait住,要在主线程中判断如果所有的子线程都是w ...
- 所有java程序都有线程_若所有的用户线程都终止了,Java程序就会结束。( )_学小易找答案...
[单选题]关于链表结构,陈述错误的是 . [单选题]下列关于构造方法的特点的描述中,错误的是 . [判断题]在构造方法中如调用 super() 语句,则必须使其成为构造方法中的第一条语句. [单选题] ...
最新文章
- Go 知识点(17)— go 工具链 go test 使用
- 刷爆 AI 圈!基于 Transformer 的 DALL-E 代码刚刚开源了
- 11Builder(构建器)模式
- Spark1.0.0 开发环境高速搭建
- wps android qq 群,手机WPS怎样发送文档给QQ?WPS怎样发送文档给QQ教程
- mybatis mysql demo_SpringBoot(入门Demo,整合mybatis,连接mysql)
- 使用TVP批量插入数据
- JavaScript中奇妙的replace
- 进程(并发,并行) join start 进程池 (同步异步)
- 软件工程实践专题第三次团队作业
- NoSQL之MongoDB安装
- Mac|技巧:Mac电脑如何使用ping命令呢?
- JDK正式免费、Log4j2爆核弹级漏洞、LayUI下线,2021的10 件大事
- 大胖子走迷宫【第十届】【决赛】
- 数学建模复习——代码记录
- php导出word文件(二)
- Linux无法终止进程,如何在Linux中终止进程
- Ubuntu16.04解决无法切换root权限的问题
- 单位冲激信号 matlab,matlab单位冲激序列
- 从零构建通讯器--7.1过往总结和心跳包代码实战
热门文章
- 算法的封装与切换——策略模式
- 常见的 OOM 原因及其解决方法(OutOfMemoryError)
- linux虚拟机克隆
- leetcode 322. Coin Change | 322. 零钱兑换(动态规划)
- leetcode 190. Reverse Bits | 190. 颠倒二进制位(移位操作,十进制二进制相互转换,打表法)
- 常见开源项目结构划分
- 牛客网_PAT乙级_1023旧键盘打字(20)【别人代码里用到的hash是啥】
- CentOS修改主机名(hostname)
- datastage 函数_DataStage_Transformer常用函数
- Linux网络编程 之 IO多路复用select(八)