@Override public void download(final com.illumina.basespace.File file, java.io.File target) { FileOutputStream fos = null; InputStream in = null; boolean canceled = false; try { final int CHUNK_SIZE = 4096; if (target.isDirectory()) { if (!target.exists() && !target.mkdirs()) { throw new IllegalArgumentException("Unable to create local folder " + target.toString()); } target = new java.io.File(target, file.getName()); } in = getFileInputStream(file); fos = new FileOutputStream(target); long progress = 0; byte[] outputByte = new byte[CHUNK_SIZE]; int bytesRead = 0; readTheFile: while ((bytesRead = in.read(outputByte, 0, CHUNK_SIZE)) != -1) { fos.write(outputByte, 0, bytesRead); progress += bytesRead; DownloadEvent evt = new DownloadEvent(this, file.getHref(), progress, file.getSize()); fireProgressEvent(evt); if (evt.isCanceled()) { canceled = true; break readTheFile; } } fos.close(); in.close(); fos = null; in = null; } catch (BaseSpaceException bs) { throw bs; } catch (Throwable t) { throw new RuntimeException("Error during file download", t); } finally { try { if (in != null) in.close(); } catch (Throwable t) { } try { if (fos != null) fos.close(); } catch (Throwable t) { } if (canceled) { if (target != null) target.delete(); DownloadEvent evt = new DownloadEvent(this, file.getHref(), 0, file.getSize()); fireCanceledEvent(evt); } else { DownloadEvent evt = new DownloadEvent(this, file.getHref(), file.getSize(), file.getSize()); fireCompleteEvent(evt); } } }
/** * download * * <p>Expose a single chunk of download to the client. Thread-safe API call. Multiple threads may * use this call to do parallel fetches of a single file. * * <p>This is a range download from file, starting at fileStart for len bytes. It will write data * into target starting at targetStart. If file is a directory, this will use * target/<file.getName()>-start-len.dat */ @Override public void download( final com.illumina.basespace.File file, long fileStart, long len, java.io.File target, long targetStart) { FileChannel fc = null; RandomAccessFile ras = null; InputStream in = null; boolean canceled = false; final int CHUNK_SIZE = 8192; // for part downloads, reduce the number of calls by 1/2. long progress = 0; try { if ((fileStart + len) > file.getSize()) { throw new Exception( "Invalid download range start(" + fileStart + ") + len (" + len + ") > file size (" + file.getSize() + ")"); } if (target.isDirectory()) { if (!target.exists() && !target.mkdirs()) { throw new IllegalArgumentException("Unable to create local folder " + target.toString()); } target = new java.io.File(target, new String(file.getName())); } in = getFileInputStream( file, fileStart, fileStart + len - 1); // These are *positions*; end at len minus 1 // Note: It sounds simple to use a FileChannel to sparsely write a file. It isn't. // The code is sensitive to the right combination of usage. I use a RandomAccessStream // and aquire a FileChannel from it. It did not work if you just use the RAS. It did not // work // if you try to open the FC w/o the RAS. It did not work if you use a RAS:FC but allocate // a ByteBuffer once, call bb.clear, bb.put, then fc.write(bb). This caused the FC to write // garbage for CHUNK_SIZE-bb.length. // Thus we now always use the ByteBuffer.wrap and the FC from a RAS. ras = new RandomAccessFile(target, "rw"); fc = ras.getChannel(); // Open for WRITE default. fc.position(targetStart); // Place the position at our place in the file fc.force(true); progress = 0; byte[] outputByte = new byte[CHUNK_SIZE]; int bytesRead = 0; readTheFile: while ((bytesRead = in.read(outputByte, 0, CHUNK_SIZE)) != -1) { fc.write(ByteBuffer.wrap(outputByte, 0, bytesRead)); progress += bytesRead; DownloadEvent evt = new DownloadEvent(this, file.getHref(), progress, len); fireProgressEvent(evt); if (evt.isCanceled()) { canceled = true; break readTheFile; } } fc.close(); ras.close(); in.close(); fc = null; ras = null; in = null; } catch (BaseSpaceException bs) { throw bs; } catch (Throwable t) { throw new RuntimeException("Error during file download", t); } finally { try { if (fc != null) fc.close(); } catch (Throwable t) { } try { if (in != null) in.close(); } catch (Throwable t) { } try { if (ras != null) ras.close(); } catch (Throwable t) { } if (canceled) { if (target != null) target.delete(); DownloadEvent evt = new DownloadEvent(this, file.getHref(), 0, len); fireCanceledEvent(evt); } else { // Could be called even if we get an exception, must pass progress, not length DownloadEvent evt = new DownloadEvent(this, file.getHref(), progress, len); fireCompleteEvent(evt); } } }