Subversion file download + subversion (svn)

Gainwar

Erfahrenes Mitglied
Hi,

bin zur Zeit dran eine Weboberfläche für Subversion zu programmieren. (Wie ViewCVS). Ich arbeite hierzu mit der aktuellen "svn-javahl-1.3", welche die neue Methode streamFileContent(...) zur Verfügung stellt.

Nun, wenn ein User auf ein File klickt bekommt er zusätzlich die Möglichkeit sich diese Datei herunterzuladen. Diese File soll nicht erst auf dem Server zwischengespeichert werden, sondern direkt an den User geschickt werden. An sich ja kein Problem, funktioniert auch gut. Das Problem ensteht erst wenn die Datei eine etwas höhere Größe hat, z.B. 80 MB und größer. Ganz klar eigentlich, den schließlich speichere ich die Datei in einem byte[] welche ich dann über ein Servlet an den User schicke. Hier ein bisschen Code meines Download Servlets:

PHP:
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		// Get request and params.
		//String filename = request.getPathInfo();
		long revision = Long.parseLong(request.getParameter("revision"));
		String path		= request.getParameter("path");
		String filename = request.getParameter("filename");
		
		// Set content type. (force download)
		response.setContentType("application/octet-stream");
		response.setHeader("Content-Disposition", "attachment; filename=" + filename + ";");
		//response.setHeader("Content-Length","...");

		// Get the output stream.
		ServletOutputStream out = response.getOutputStream();
		
		// Get current instance of FacesContext and Application.
		FacesContext fc = FacesContext.getCurrentInstance();
		Application	app = fc.getApplication();
		
		// Get controller and connector.
		ControllerBean controller = (ControllerBean)app.createValueBinding("#{Controller}").getValue(fc);
		SVNConnector svn_connector = (SVNConnector)controller.getConnector();
		
		// Subversion repository url.
		String strRepos = svn_connector.getRepository();
		
		// File content.
		byte[] content = svn_connector.getFileContent( strRepos + path, Revision.getInstance(revision));
		
		// Initialize download.
        out.write( content );
        out.flush();
        out.close();

	}

Eigentlich liegt der Fehler auf der Hand, die Zwischenspeicherung im byte[] verursacht den Heap overflow. Falsch, der Fehler passiert bereits in der von der svn-javahl gegebenen Methode "streamFileContent", welche mir meinen übergebenen OutputStream füllt. Habe mir auch den native Code angesehen. Das Problem ist die Methode füllt den Stream komplett, womit ich keine Chance habe eine Bufferung vorzunehmen. Hier der native Codeteil welcher den Stream füllt:

PHP:
   while (contentSize > 0)
   {
       size_t readSize = bufSize > contentSize ? contentSize : bufSize;
       Err = svn_stream_read(read_stream, (char *)bufData, &readSize);
       if (Err != NULL)
       {
           env->ReleaseByteArrayElements(buffer, bufData, 0);
           svn_stream_close(read_stream);
           JNIUtil::handleSVNError(Err);
           return;
       }

       env->ReleaseByteArrayElements(buffer, bufData, JNI_COMMIT);
       env->CallVoidMethod(outputStream, writeMethod, buffer, 0, readSize);
       if (JNIUtil::isJavaExceptionThrown())
       {
           env->ReleaseByteArrayElements(buffer, bufData, 0);
           svn_stream_close(read_stream);
           return;
       }
       contentSize -= readSize;
   }

Nun wäre meine Frage, wie kann ich den Stream -direkt- an den User zum Download weiterleiten, ohne vorher eine Working copy der File auf dem Server anzulegen.

Vielen Dank, Gruß
- Manuel

PS: Entschuldigung das ich PHP-Code boxen genommen habe, aber da dies die einzigen sind welche ein Highlight benutzen, nahm ich diese damit ihr den Code besser lesen könnt.
 
Hallo!

Wie man eine Datei zu einem Client streamen kann findest du hier:
http://www.tutorials.de/forum/java/205369-blob-aus-mysql-browser-darstellen.html?highlight=Download

Die SVNConnector Klasse scheint eine Eigenenentwicklung von dir zu sein, bzw. kann ich diese nicht im javah SVN Verzeichnis finden:
http://svn.collab.net/viewcvs/svn/t...java/javahl/src/org/tigris/subversion/javahl/
Schaut man sich das SVNClientInterface - Interface an: http://svn.collab.net/viewcvs/svn/t...SVNClientInterface.java?rev=17860&view=markup
So findet man die von dir genannte Methode:
Code:
   /**
     * Write the file's content to the specified output stream.  If
     * you need an InputStream, use a
     * PipedInputStream/PipedOutputStream combination.
     *
     * @param path        the path of the file
     * @param revision    the revision to retrieve
     * @param pegRevision the revision at which to interpret the path
     * @param the stream to write the file's content to
     * @throws ClientException
     * @see <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PipedOutputStream.html">PipedOutputStream</a>
     * @see <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/PipedInputStream.html">PipedInputStream</a>
     */
    void streamFileContent(String path, Revision revision, Revision pegRevision,
                           int bufferSize, OutputStream stream) 
        throws ClientException;
In deinem geposteten Codeschnippsel kann ich den Aufruf dieser Methode leider nicht entdecken. (Hast du die vielleicht am SVNConnector implementiert?) Vielleicht solltest du diese Methode einfach mal direkt verwenden via SVNClient.

Gruss Tom
 
Hi,

die Methode "streamFileContent" rufe ich über meine "getFileContent" auf:

PHP:
	public byte[] getFileContent(String path, Revision rev){

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			client.streamFileContent(path, rev, rev, 256, out);
		} catch (ClientException e) {
			jlog.error("Can't get file content from subversion.");
			e.printStackTrace();
		}
		// TODO: The content should NOT be saved in a variable.
		// TODO: The return type should be void or a stream.
		// TODO: Big files would cause an java heap space exception.
		byte[] content = out.toByteArray();
		return content;
	}
--> client = SVNClient();

So läuft es im Moment. ( nur mit kleinen Files )
Hatte es auch schon mit PipedOutput/Input -Streams, allerdings helfen diese auch nichts, weil die Methode "streamFileContent" den overflow verursacht. Auch Threads scheinen in diesem Fall nichts zu bringen da ich nicht in den Prozess der native File eingreifen kann. Alles schon versucht :)

Gruß
- Manuel
 
Hi,
kann dir das Projekt leider nicht schicken - siehe PM.
Für das Problem hier dennoch weiter, evtl. weis jemand anders bescheid oder man kann jemanden helfen.

In deinem Tutorial hast du alles ja schön beschrieben, das Problem ist nur dass ich durch Subversion keine reale Datei habe sondern nur einen vollen Stream und somit keinen File Stream erstellen kann. Bin gerade noch dabei ViewCVS Source zu durchstöbern um zu sehen wie diese das Problem gelöst haben.

Gruß
- Manuel
 
Hallo!

Hast du es schonmal mit:
Code:
ServletOutputStream out = response.getOutputStream();
...
streamFileContent(strRepos + path,Revision.getInstance(revision), XXXX,
                           16384, out);
...
versucht?

Gruss Tom
 
Hi!

Jopp, schon versucht und auch schon mit dem response.getWriter().
Zudem tritt der overflow innerhalb der Methode streamFileContent auf, was bedeutet ich komm nicht weiter um den Stream in irgend einer Art zu Weise zu behandeln.

Vermute ich brauche einfach eine andere Methode, aber ich schau mich noch um.

Gruß
- Manuel
 
Hallo!

Folgendes:
Code:
package de.tutorials;

import java.io.File;
import java.io.FileOutputStream;

import org.tigris.subversion.javahl.Revision;
import org.tigris.subversion.javahl.SVNClient;
import org.tigris.subversion.javahl.SVNClientInterface;

public class SVNRepositoryDownloadExample {
    /**
     * @param args
     */
    public static void main(String[] args) throws Exception{
        SVNClientInterface svnClient = new SVNClient();
        svnClient.username("tom");
        svnClient.password("tom");
        FileOutputStream fileOutputStream = new FileOutputStream(new File("c:/Nordwind.mdb"));
        svnClient.streamFileContent("svn://localhost/de.tutorials.svn/Nordwind.mdb",Revision.getInstance(7L),Revision.getInstance(7L),16384,fileOutputStream);
        fileOutputStream.close();
    }
}
...funktioniert mit einer (künstlich etwas aufgeblähten) 15 MB großen Nordwind.mdb einwandfrei. Ich denke so solltest du auch von einem Servlet aus Streamen können.

Gruss Tom
 
Hi,
leider muss ich dir sagen das diese Möglichkeit auch nicht funktioniert.
Das Problem liegt einfach an der native Methode in die nicht eingegriffen werden kann.

Werd noch ein paar andere Möglichkeiten versuchen und bescheid geben wenn ich eine Lösung gefunden habe.

Gruß Manuel
 
Hallo!

Ich wette das funktioniert genau so wie oben beschrieben... ob ich nun die Datei in ein File streame oder ueber ein Servlet zum Client schicke ist doch egal ;-)
Ich baue heute Abend mal ein Beispiel dazu.
Ich habe uebrigens die entsprechenden Jars aus dem Subclipse Eclipse-Plugin verwendet.

Gruss Tom
 
Zurück