【问题标题】:Invalid JPEG file structure: two SOI markers error?JPEG 文件结构无效:两个 SOI 标记错误?
【发布时间】:2015-06-28 17:37:54
【问题描述】:

This is the image that causes an error

我遇到了这个问题,我尝试了我所知道的一切,但没有任何效果。 我正在尝试通过套接字从数据库向客户端应用程序发送多个图像(一次图像),有时一切正常,但有时它会声明此“无效的 JPEG 文件结构:两个 SOI 标记”错误?

客户端:

for(User user : users){
    int cmpt=0;

    byteToread =in.readInt();
    bytesOut=new ByteArrayOutputStream();
    bos = new BufferedOutputStream(bytesOut);    

    while (byteToread >cmpt) {
        cmpt = in.read(dataEntre);
        bos.write(dataEntre, 0, cmpt);
        bos.flush();
        byteToread-=cmpt;
    } 
    BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytesOut.toByteArray()));

    user.setPhoto(new ImageIcon(bi));
    System.out.println("------------------end");
}
bos.close();
bytesOut.close();

服务器端:

InputStream input =null;

Statement myStmt = null;
ResultSet myRs = null;
BufferedInputStream bis=null;
try {
    myStmt = Conn.createStatement();
    myRs = myStmt.executeQuery("select Photo from users order by Name");
    byte[] buffer; 

    int k =1;

    while (myRs.next()) {
        input=myRs.getBinaryStream("Photo");
        bis = new BufferedInputStream(input);
        buffer = new byte[1024];

        try {
            int byteToread = 0;
            int cmpt=0;

            byteToread=input.available();
            out.writeInt(byteToread);
            out.flush(); 

            int i=0;  
            while (byteToread>cmpt) {
                cmpt = bis.read(buffer);
                out.write(buffer, 0, cmpt);
                out.flush();
                byteToread -= cmpt;
            } 
        } catch (IOException ex) {
            return ;
        }
    }

【问题讨论】:

  • 这个问题最近才出现,我的应用程序正常工作了将近 2 个月,但现在这个错误不知从何而来
  • 可能是您使用的图像在 2 个月内没有触发问题。你在问什么并不完全清楚。还建议链接导致失败的图像,以防有人想自己测试。 (当然假设图像没有版权)
  • 好吧,我会感谢你的,那我写对了代码呢,特别是客户端
  • SOI 标记出现在流中的什么位置?您是否运行了一些 JPEG 转储程序来查找市场订单?
  • 使用ImageIO.read(new File("path/to/your/file.jpg")) 可以很好地读取图像(并且应该只有一个 SOI 标记)。因此,正如 EJP 和 JoeBlade 都指出的那样,错误出在您的字节洗牌代码中......

标签: java sockets jpeg


【解决方案1】:

不要使用available() 作为输入长度的度量。 Javadoc 中有针对此的特定警告。这不是它的用途。

您的复制循环应如下所示:

while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

【讨论】:

    【解决方案2】:

    首先要做的事情: 你遇到的错误:

    每个 JPEG 图像总是以 StartOfImage 标记开始,十六进制为:FFD8 您正在创建的缓冲区中的字节包含此 SOI 标记两次。 您很可能得到图像 1 的字节 + 图像 2 的一些字节,特别是包含第二个图像标记的起始字节。

    我认为你在这里犯了一个错误:

    byteToread =in.readInt();
    bytesOut=new ByteArrayOutputStream();
    bos = new BufferedOutputStream(bytesOut);     
    while (byteToread >cmpt) {
        cmpt = in.read(dataEntre);
        bos.write(dataEntre, 0, cmpt);
        bos.flush();
        byteToread-=cmpt;
    } 
    

    以您总共有 100 个字节的情况为例。 出于某种原因,您将以 50、30、20 字节为单位读取这 100 个字节。

    这将给出以下内容:

    iteration 1:
    during while:   
        bytesToRead: 100
        cmpt: 0
    at the end of the loop:
        bytesToRead: 50
        cmpt: 50
    iteration 2:
    during while:   
        bytesToRead: 50
        cmpt: 50
    

    此时,byteToRead (50) 不 > cmpt (50),因此当您不写入最后 50 个字节时,循环结束。

    你可能需要类似的东西:

    bytesToread = in.readInt();
    bytesOut=new ByteArrayOutputStream();
    bos = new BufferedOutputStream(bytesOut);     
    while (byteToread > 0) {
        cmpt = in.read(dataEntre);
        bos.write(dataEntre, 0, cmpt);
        bos.flush();
        byteToread-=cmpt;
    }
    

    可能对于小图像,您可以一次性读取全部字节数(虽然不能保证,但很有可能)。如果发生这种情况,那么您将在一次迭代中完成并且永远不会遇到问题。但是对于较大的图像,您可能会面临读取的最后一个块会导致您在图像末尾丢失一些字节的风险。这可能会造成麻烦(我认为)

    第二个问题可能是(在服务器端)

    byteToread=input.available();
    

    您从 db 获得的结果集可能还没有所有字节可供您使用。

    来自 available() 的文档

    请注意,虽然 InputStream 的某些实现会返回流中的总字节数,但很多不会。使用此方法的返回值来分配旨在保存此流中所有数据的缓冲区是不正确的。

    据我所知,这是错误的(并且基于我的测试程序),会发生以下情况:

    1:你得到 image1(比如 200 字节)。 2:您将长度(200)写入输出流 3:您向输出流写入少于 200 个字节(例如 150 个)。 (因为逻辑错误) 4:你得到 image2(比如 300 字节) 5:将长度(300)写入输出流 6:将 N 个字节写入输出流。

    当你阅读时

    1:您阅读了 image1.length。这将返回 200。 2:你读了 200 个字节。因为您只从图像 1 中写入了 150 字节,这意味着您从图像 1 中获得 150 字节,图像 2 的长度,然后从图像 2 中获得 46 字节。

    这会导致您认为是图像 1 的字节包含 2 个 jpg 标头。 (毕竟 image2 的前 46 个字节将包含 image2 的标头)

    我认为您需要附加调试器并逐步执行此操作并找出其他问题。给出的另一个答案显示了如何正确循环并计算读取的字节数。可能还有更多问题。

    【讨论】:

    • 是的,我也这样做了,但就像你说的那样,它会导致丢失一些字节,我编写的代码大部分时间都可以工作,但有时却不行,
    • 在我的回答中,我解释了为什么(我认为)它有时有效而其他时候无效。您无法随时控制应用程序读取的字节数。尝试使用 while (byteToRead > 0) 看看它是否效果更好。
    • 我做到了,服务器确实发送了每个图像和每个字节,客户端应用程序没有声明任何错误,但是执行不会继续下一步或下一条指令,就像输入套接字一样等待更多数据,但服务器已经发送每一个并继续前进
    • 实际上在重读时,您在客户端和服务器端都犯了这个错误。双方都应该使用 while (bytesToread > 0) 而不是 bytesToRead > compt
    • 是的,我在双方都使用了 while(bytesToread>0) ,它没有声明任何错误,但就像我说的客户端不要继续执行,也不要停止,这就像在等待来自服务器的数据
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-20
    • 2020-03-05
    • 2020-12-06
    • 2020-03-22
    • 1970-01-01
    相关资源
    最近更新 更多