(String url, final String fileName, final OnComplete<Integer> percentageCallback, final OnComplete<String> filesavedCallback)
| 3155 | /// |
| 3156 | /// - `IOException` |
| 3157 | public static void downloadUrlSafely(String url, final String fileName, final OnComplete<Integer> percentageCallback, final OnComplete<String> filesavedCallback) throws IOException { |
| 3158 | // Code discussion here: https://stackoverflow.com/a/62137379/1277576 |
| 3159 | String partialDownloadsDir = FileSystemStorage.getInstance().getAppHomePath() + FileSystemStorage.getInstance().getFileSystemSeparator() + "partialDownloads"; |
| 3160 | if (!FileSystemStorage.getInstance().isDirectory(partialDownloadsDir)) { |
| 3161 | FileSystemStorage.getInstance().mkdir(partialDownloadsDir); |
| 3162 | } |
| 3163 | final String uniqueId = url.hashCode() + "" + downloadUrlSafelyRandom.nextInt(); // do its best to be unique if there are parallel downloads |
| 3164 | final String partialDownloadPath = partialDownloadsDir + FileSystemStorage.getInstance().getFileSystemSeparator() + uniqueId; |
| 3165 | final boolean isStorage = fileName.indexOf("/") < 0; // as discussed here: https://stackoverflow.com/a/57984257 |
| 3166 | final long fileSize = getFileSizeWithoutDownload(url, true); // total expected download size, with a check partial download support |
| 3167 | final int splittingSize = 512 * 1024; // 512 kbyte, size of each small download |
| 3168 | final Wrapper<Long> downloadedTotalBytes = new Wrapper<Long>(0l); |
| 3169 | final OutputStream out; //NOPMD CloseResource - stream closed when download completes |
| 3170 | if (isStorage) { |
| 3171 | out = Storage.getInstance().createOutputStream(fileName); // leave it open to append partial downloads |
| 3172 | } else { |
| 3173 | out = FileSystemStorage.getInstance().openOutputStream(fileName); |
| 3174 | } |
| 3175 | final EasyThread mergeFilesThread = EasyThread.start("mergeFilesThread"); // Codename One thread that supports crash protection and similar Codename One features. |
| 3176 | |
| 3177 | final ConnectionRequest cr = new GZConnectionRequest(); |
| 3178 | cr.setUrl(url); |
| 3179 | cr.setPost(false); |
| 3180 | if (fileSize > splittingSize) { |
| 3181 | // Which byte should the download start from? |
| 3182 | cr.addRequestHeader("Range", "bytes=0-" + splittingSize); |
| 3183 | cr.setDestinationFile(partialDownloadPath); |
| 3184 | } else { |
| 3185 | Util.cleanup(out); |
| 3186 | if (isStorage) { |
| 3187 | cr.setDestinationStorage(fileName); |
| 3188 | } else { |
| 3189 | cr.setDestinationFile(fileName); |
| 3190 | } |
| 3191 | } |
| 3192 | cr.addResponseListener(new ActionListener<NetworkEvent>() { |
| 3193 | @Override |
| 3194 | public void actionPerformed(NetworkEvent evt) { |
| 3195 | mergeFilesThread.run(new Runnable() { |
| 3196 | @Override |
| 3197 | public void run() { |
| 3198 | try { |
| 3199 | // We append the just saved partial download to the fileName, if it exists |
| 3200 | if (FileSystemStorage.getInstance().exists(partialDownloadPath)) { |
| 3201 | InputStream in = null; //NOPMD CloseResource |
| 3202 | try { |
| 3203 | in = FileSystemStorage.getInstance().openInputStream(partialDownloadPath); |
| 3204 | Util.copyNoClose(in, out, 8192); |
| 3205 | } finally { |
| 3206 | Util.cleanup(in); |
| 3207 | } |
| 3208 | // before deleting the file, we check and update how much data we have actually downloaded |
| 3209 | downloadedTotalBytes.set(downloadedTotalBytes.get() + FileSystemStorage.getInstance().getLength(partialDownloadPath)); |
| 3210 | FileSystemStorage.getInstance().delete(partialDownloadPath); |
| 3211 | } |
| 3212 | // Is the download finished? |
| 3213 | if (downloadedTotalBytes.get() > fileSize) { |
| 3214 | throw new IllegalStateException("More content has been downloaded than the file length, check the code."); |
no test coverage detected