Avatar billede andersbl Nybegynder
13. august 2010 - 14:49 Der er 20 kommentarer og
1 løsning

Download en fil fra sql 2008 database

Hej
Jeg har ledt hele dagen for at finde ud af hvordan jeg downloader en fil fra databasen. Tabellen ser således ud

CREATE TABLE [dmbd_owner].[testFiles](
    [id] [tinyint] NOT NULL,
    [type] [varchar](3) NOT NULL,
    [file] [varbinary](max) NOT NULL
)

==============================================================
Siden jeg uploader filen mer er følgende
==============================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@page language="java" contentType="text/html; charset=ISO-8859-1"%>
<%@ page import="java.util.*, java.io.*, java.sql.*, test.*, java.text.NumberFormat, java.lang.reflect.InvocationTargetException" %>
<%@page import="test.*"%>
<html>
<head>
<title>run</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="GENERATOR" content="Rational Application Developer">
</head>
<body>
 
  <form action="run.jsp" method="POST">
    <input type="file" name="attach"><br>
    <input type="Submit" value="Upload File"><br>
  </form>
<%
if (request.getParameter("attach") != null) {
Connection conn = forbindelse.getConnection();
String path = request.getParameter("attach");

out.write("\nPath: " + path);

PreparedStatement pstmt = conn.prepareStatement("INSERT INTO dmbd_owner.testFiles VALUES (?,?,?)");
pstmt.setInt(1, 11);
pstmt.setString(2, "aa");
File fBlob = new File(path);

FileInputStream is = new FileInputStream (fBlob);
pstmt.setBinaryStream(3, is, (int) fBlob.length() );
pstmt.execute();

out.write("\nINDSAT FIL");
}
%>
</body>
</html>

==============================================================
Og måden jeg downloader på er følgende:
==============================================================
<%@ page import="java.io.*,java.util.*,java.sql.*" %>
<%
int id=0;
if(request.getParameter("id")!=null && request.getParameter("id")!="")
{
id = Integer.parseInt(request.getParameter("id").toString());
}
String connectionURL = "jdbc:mysql://localhost/application";
String url=request.getParameter("WEB_URL");
String Content=new String("");
Statement stmt=null;
Connection con=null;
try
{
String filename="data"+id+".txt";
//Class.forName("com.mysql.jdbc.Driver").newInstance();
con=dmbd.DBcon.getConnection();
stmt=con.createStatement();
String qry = "SELECT * FROm dmbd_owner.testFiles WHERE id="+id;
ResultSet rst= stmt.executeQuery(qry);
if(rst.next())
{

Content=rst.getString("file");
}
out.println(Content);
byte requestBytes[] = Content.getBytes();
ByteArrayInputStream bis = new ByteArrayInputStream(requestBytes);
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);

byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) > 0){
response.getOutputStream().write(buf, 0, len);
}
bis.close();
response.getOutputStream().flush();
}
catch(Exception e){
e.printStackTrace();
}
%>
==============================================================


Jeg har aldrig leget med upload/download før så ved ikke om det er i upload fasen den faktisk fejler.
min kode fejler "lidt", dvs. får mulighed for at downloade fil, men får også følgende

java.lang.IllegalStateException: getOutputStream() has already been called for this response
    at org.apache.catalina.connector.Response.getWriter(Response.java:596)
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:186)
    at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:124)

Filen jeg henter indeholder:
544553542046494C2054494C20646F776E6C6F6164
Hvor der skulle stå en meget simpel text skrevet i TXT.

Funktionen skal kunne håndterer alle slags filer.
Avatar billede arne_v Ekspert
13. august 2010 - 15:23 #1
Det her ser lidt suspekt ud:

out.println(Content);
byte requestBytes[] = Content.getBytes();
ByteArrayInputStream bis = new ByteArrayInputStream(requestBytes);
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);

byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) > 0){
response.getOutputStream().write(buf, 0, len);
}
bis.close();
response.getOutputStream().flush();
}

Du starter med at skrive til out og saa resetter du og forsoeger at outputte igen.

Hvis det foerste skriv er sendt til browser saa er det ikke muligt at resette.
Avatar billede arne_v Ekspert
13. august 2010 - 15:25 #2
Alle de mange linier kunne nok erstattes af:

response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);
byte[] buf = Content.getBytes();
response.getOutputStream().write(buf, 0, buf.length);
response.getOutputStream().flush();
Avatar billede arne_v Ekspert
13. august 2010 - 15:25 #3
Derudover finder jeg rst.getString meget mistaenkeligt for binaere data !!
Avatar billede andersbl Nybegynder
15. august 2010 - 16:20 #4
Hvis det ikke skal string hvad så? De eksempler jeg har set er med blob og det går jeg ud fra jeg ikke kan benytte.
Jeg prøver det du har forslået i morgen.
Avatar billede arne_v Ekspert
15. august 2010 - 16:26 #5
Enten getBlob eller getBytes.
Avatar billede andersbl Nybegynder
16. august 2010 - 10:12 #6
Nu har jeg gjort som du forslog og har brugt getBytes. Det virker som det skal MEN for stadig samme fejl.

===================== DOWNLOAD JSP SIDE ========================
<%@ page import="java.io.*,java.util.*,java.sql.*" %>
<%
int id=0;
if(request.getParameter("id")!=null && request.getParameter("id")!="")
{
id = Integer.parseInt(request.getParameter("id").toString());
}
String connectionURL = "jdbc:mysql://localhost/application";
String url=request.getParameter("WEB_URL");
byte[] buf = null;
Statement stmt=null;
Connection con=null;
try
{
String filename="data"+id+".png";
con=forbindelse.getConnection();
stmt=con.createStatement();
String qry = "SELECT * FROm testFiles WHERE id="+id;
ResultSet rst= stmt.executeQuery(qry);
if(rst.next())
{
    buf=rst.getBytes("file");
}
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);
response.getOutputStream().write(buf, 0, buf.length);
response.getOutputStream().flush();
response.getOutputStream().close();
}
catch(Exception e){
    //System.out.println("Fejl ved hentning af fil: " + e);
}
%>

===================== FEJL FRA JSP SIDE ========================
SEVERE: Servlet.service() for servlet jsp threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
    at org.apache.catalina.connector.Response.getWriter(Response.java:596)
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:186)
    at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:124)
    at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:117)
    at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:191)
    at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:115)
    at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:75)
    at org.apache.jsp.test.file_005fdownload_jsp._jspService(org.apache.jsp.test.file_005fdownload_jsp:111)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:322)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:291)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:744)
    at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
    at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
    at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
    at java.lang.Thread.run(Thread.java:810)


Et lille tillægsspørgsmål :)
Er også om der er en smart måde at bestemme hvad fil typen er. Kan selvfølgelig indsætte det i databasen når de uploader filen, men er der en anden måde man plejer at gøre sådan noget på?
Avatar billede arne_v Ekspert
16. august 2010 - 10:45 #7
hvad sker der hvis du erstatter response.getOutputStream() med out ?
Avatar billede andersbl Nybegynder
16. august 2010 - 11:11 #8
Ser ikke det er muligt. Findes ikke en out funktion til disse funktioner

response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);
response.getOutputStream().write(buf, 0, buf.length);

men findes til:
response.getOutputStream().flush();
response.getOutputStream().close();
Avatar billede arne_v Ekspert
16. august 2010 - 11:25 #9
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);
out.write(bla bla);
Avatar billede andersbl Nybegynder
16. august 2010 - 11:30 #10
Tror ikke helt jeg kan følge dig for har nu skrevet sådan:

response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);
out.write("test");

og det giver selvfølgelig ikke nogen brugbar fil, men fejlmeddelsen er væk.
Avatar billede arne_v Ekspert
16. august 2010 - 13:56 #11
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment; filename=" +filename);
out.write(buf, 0, buf.length);

?
Avatar billede andersbl Nybegynder
16. august 2010 - 14:31 #12
findes ikke en metode som hedder out.write(buf, 0, buf.length);
Den metode der minder om den er:
out.write(char[] cbuf,int off,int len)

men som du kan se indeholder den ikke et byte[] men char[].
Avatar billede arne_v Ekspert
17. august 2010 - 21:38 #13
Ugh.

JSP og binaere data er ikek en god kombo.

Kan du ikke omskrive den JSP til servlet?

og saa bruge response.getOutputStream()
Avatar billede andersbl Nybegynder
17. august 2010 - 21:46 #14
Jeg vil prøve at gøre det på torsdag.
Går ud fra det stadig er muligt at trykke på et link og downloade filen UDEN at den skal loade en nye side. Uden jeg tænkte så meget over det fungerede JSP-siden nemlig på den måde og det var godt.

men som jeg husker det er JSP en slags omvendt Servlet, så virker det i JSP kan det virke på samme måde i Servlet.
Avatar billede arne_v Ekspert
17. august 2010 - 21:55 #15
JSP sider compiles til en servlet.

Men JSP laver en masse antagelser bl.a. om at den kan antage tekst etc..
Avatar billede arne_v Ekspert
26. september 2010 - 21:05 #16
Kommet videre?
Avatar billede andersbl Nybegynder
12. oktober 2010 - 12:59 #17
Ja, det fungerer, men er støt på et nyt problem.

Når jeg uploader en fil får jeg en for kort værdi indlæst i tabellen.

Denne fungerer

    public static void insertAttachment(String caseNo, String path) throws SQLException {

          String[] temp = path.split("\\\\");
          int i = temp.length;
          File fBlob = new File(path);
         
          FileInputStream is;
        try {
            is = new FileInputStream (fBlob);
              Connection con = null;
              con = DBcon.getConnection();
              PreparedStatement pstmt = con.prepareStatement("INSERT INTO dmbd_owner.testFiles (alertId, filename, [file]) VALUES (?,?,?)");
              pstmt.setString(1, caseNo);
              pstmt.setString(2, temp[i-1]);
              pstmt.setBinaryStream(3, is, (int) fBlob.length() );
              pstmt.execute();
          } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
            e.printStackTrace();
        }
     
    }



Jeg vil i stedet bruge Stored Procedure og her fejler den.
SP

ALTER PROCEDURE [dmbd_owner].[dcmAlert_insertAttatcment]
    @caseNo                int,
    @filename            varchar(30),
    @file                varbinary(max)

AS
    INSERT INTO dmbd_owner.testFiles    (
                                            alertId,
                                            filename,
                                            [file]
                                        )
    VALUES    (
                @caseNo,
                @filename,
                @file
            )


JAVA-kode

      public static void insertAttachment(String caseNo, String path) throws SQLException {

          String[] temp = path.split("\\\\");
          int i = temp.length;
          File fBlob = new File(path);
         
          Connection con = null;
        CallableStatement cstmt = null;

        try {
            FileInputStream is = new FileInputStream (fBlob);
            con = DBcon.getConnection();
                cstmt = con.prepareCall("{call dcmAlert_insertAttatcment(?,?,?)}");
                cstmt.setString(1, caseNo);
                cstmt.setString(2, temp[i-1]);
                    System.out.println("fil: " +is);
                cstmt.setBinaryStream(3, is, (int) fBlob.length() );
                cstmt.executeUpdate();
            con.commit();
        } catch (Exception e) {
            con = null;
            System.out.println("DCM.insertAlert: " + e);
        }
        con.close();
        cstmt.close();
       
    }


Den indlæser fx i den binary værdi "0xFF" hvor den metode som fungerer der indsætter den "0xFFFE55005300450020005B0064006D...."
Avatar billede arne_v Ekspert
17. oktober 2010 - 03:09 #18
Umiddelbart ser koden OK ud.

For det som spørgsmålet drejer sig om. Din håndtering ef fejl og close er ikke optimal.

Har du checket om der er en JDBC opdatering?

Kunne du evt. prøve om setBytes fremfor setBinaryStream virker?

(setBytes er ikke god for meget store filer, men vi prøver os lidt frem)
Avatar billede arne_v Ekspert
17. oktober 2010 - 03:10 #19
Du kunne også prøve med IMAGE fremfor VARBINARY(MAX) i SP.

Det burde være det samme, men man ved aldrig.
Avatar billede andersbl Nybegynder
17. oktober 2010 - 10:36 #20
jeg prøver mig med dine forslag, men det er nok en god ide, at oprette et nyt spørgsmål.

Smider du lige et svar så du kan få nogle point?
Avatar billede arne_v Ekspert
17. oktober 2010 - 15:30 #21
ok
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester