Download file from API server using URLConnection in Kotlin - java

I want to download a file into my library using URLConnection.
The size of the picture stored in the server is about 16mb, but the size of the saved picture is 84B.
There seems to be an error while reading the buffer, can you tell me what kind of error it is?
binding.signDownloadBtn.setOnClickListener {
val userId = binding.userIdText.text.toString()
val filetype = "user"
val fileSeparation = "sign"
Thread {
val spec = "myurl/${userId}/${filetype}/${fileSeparation}"
val outputDir = Environment.getExternalStorageDirectory().toString() + "/" + Environment.DIRECTORY_DOWNLOADS
var `is`: InputStream? = null
var os: FileOutputStream? = null
try {
val url = URL(spec)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "GET"
conn.setRequestProperty("Content-Type", "application/json;utf-8")
conn.setRequestProperty("Accept", "application/json")
conn.setRequestProperty("token", user_token)
conn.setRequestProperty("sysCd", sysCd)
conn.connectTimeout = 1500
println("responseCode ${conn.responseCode}")
if (conn.responseCode == HttpURLConnection.HTTP_OK) {
var fileName = ""
val disposition = conn.getHeaderField("Content-Disposition")
val contentType = conn.contentType
if (disposition != null) {
val target = "filename="
val index = disposition.indexOf(target)
if (index != -1) {
fileName =
disposition.substring(index + target.length)
fileName =
fileName.replace("\"", "")
}
println("Content-Type = $contentType")
println("Content-Disposition = $disposition")
println("fileName = $fileName")
`is` = conn.inputStream
os = FileOutputStream(File(outputDir, fileName))
val BUFFER_SIZE = 4096
var bytesRead: Int
val buffer = ByteArray(BUFFER_SIZE)
while (`is`.read(buffer).also { bytesRead = it } != -1) {
os.write(buffer, 0, bytesRead)
}
os.close()
`is`.close()
}
println("File downloaded")
} else {
println("No file to download. Server replied HTTP code: ${conn.responseCode}")
}
conn.disconnect()
} catch (e: Exception) {
println("An error occurred while trying to download a file.")
e.printStackTrace()
try {
`is`?.close()
os?.close()
} catch (e1: IOException) {
e1.printStackTrace()
}
}
}.start()
}

Related

Java HttpURLConnection http timeout

In my local it works perfectly, but when I deploy it gives me this error
nested exception is java.net.ConnectException: Connection timed out (Connection timed out)
with https everything works normal, but http does not work and it gives me the timeout error.
I also just did the tests with restTemplate, OkHttpClient and I get the same result
What am I doing wrong or what should I configure to work, I hope your help, I would be too grateful
public String getFile(String baseName, String extensioFile) {
String rpt;
int BUFFER_SIZE = 4096;
String urlDonwload = "http://datos.susalud.gob.pe/node/223/download";
try {
URL url = new URL(urlDonwload);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)");
httpConn.setConnectTimeout(900000);
httpConn.setReadTimeout(7200000);
int responseCode = httpConn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
String fileName = "";
String disposition = httpConn.getHeaderField("Content-Disposition");
if (disposition != null) {
// extracts file name from header field
int index = disposition.indexOf("filename=");
if (index > 0) {
fileName = disposition.substring(index + 10, disposition.length() - 1);
}
} else {
// extracts file name from URL
// fileName = urlCamaUci.substring(urlCamaUci.lastIndexOf("/") + 1,
// urlCamaUci.length());
LocalDateTime currentDate = LocalDateTime.now(ZoneOffset.of("-05:00"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = currentDate.format(formatter);
System.out.println();
fileName = baseName + "_" + formatDateTime.replace(" ", "_").replace(":", "-") + "." + extensioFile;
}
InputStream inputStream = httpConn.getInputStream();
// String saveFilePath = PATH + File.separator + fileName;
File pathSave = new File(fileName);
FileOutputStream outputStream = new FileOutputStream(pathSave.getCanonicalPath());
int bytesRead = -1;
byte[] buffer = new byte[BUFFER_SIZE];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.close();
inputStream.close();
rpt = pathSave.getCanonicalPath();
} else {
rpt = "FAILED";
}
httpConn.disconnect();
} catch (Exception e) {
System.out.println("error search path");
System.out.println(e.getMessage());
rpt = "FAILED";
}
return rpt;
}

How to zip folder and sub-folder using zip4j and outputstream

In the program that I am writing, I take the uri of the location to save the zip file from a user. Then, I try to zip the files and folders using zip4j library and outpustream in Android. I modified the code in this Stackoverflow answer and used zip4j instead. My modified code produces the zip file, however it is corrupted.
this is my code written in Kotlin:
class ZipBuilder {
private fun buildZipParameters(compressionMethod: CompressionMethod, compressionLevel: CompressionLevel,
encrypt: Boolean,
encryptionMethod: EncryptionMethod?,
aesKeyStrength: AesKeyStrength?
): ZipParameters? {
val zipParameters = ZipParameters()
zipParameters.compressionMethod = compressionMethod
zipParameters.compressionLevel = compressionLevel
return zipParameters
}
fun zipFileAtPath(sourcePath: String?, toLocation: ParcelFileDescriptor?): Boolean {
println("zipFileAtPath is called")
val BUFFER = 2048
val sourceFile = File(sourcePath!!)
val zipParameters = buildZipParameters(CompressionMethod.DEFLATE, CompressionLevel.NORMAL,
false, null, null)
try {
var origin: BufferedInputStream? = null
val desc = toLocation
val dest = FileOutputStream(desc!!.fileDescriptor)
val out = ZipOutputStream(BufferedOutputStream(dest))
if (sourceFile.isDirectory) {
zipParameters.rootFolderNameInZip = sourcePath
zipSubFolder(out, sourceFile, sourceFile.parent!!.length, zipParameters!!)
} else {
val data = ByteArray(BUFFER)
val fi = FileInputStream(sourcePath)
origin = BufferedInputStream(fi, BUFFER)
zipParameters!!.fileNameInZip = getLastPathComponent(sourcePath)
zipParameters.lastModifiedFileTime = sourceFile.lastModified()
out.putNextEntry(zipParameters)
var count: Int = 0
while (fi.read(data).also({ count = it }) != -1) {
out.write(data, 0, count)
}
}
out.close()
} catch (e: java.lang.Exception) {
e.printStackTrace()
return false
}
return true
}
#Throws(IOException::class)
private fun zipSubFolder(
out: ZipOutputStream, folder: File, basePathLength: Int, zipParameters: ZipParameters
) {
val BUFFER = 2048
val fileList = folder.listFiles()
var origin: BufferedInputStream
fileList?.forEach { file ->
if (file.isDirectory) {
zipSubFolder(out, file, basePathLength, zipParameters)
} else {
val data = ByteArray(BUFFER)
val unmodifiedFilePath = file.path
val relativePath = unmodifiedFilePath
.substring(basePathLength)
val fi = FileInputStream(unmodifiedFilePath)
origin = BufferedInputStream(fi, BUFFER)
zipParameters.fileNameInZip = relativePath
zipParameters.lastModifiedFileTime = file.lastModified()
out.putNextEntry(zipParameters)
var count: Int = 0
while (fi.read(data).also({ count = it }) != -1) {
out.write(data, 0, count)
}
origin.close()
}
}
}
fun getLastPathComponent(filePath: String): String? {
val segments = filePath.split("/").toTypedArray()
return if (segments.size == 0) "" else segments[segments.size - 1]
}
}
I would appreciate it if someone could tell me what could be the problem.
I guess you forgot to closeEntry() after writing date for that particular entry. You need to do out.closeEntry() after the while loop of writing data. Have a look at the example here.
while (fi.read(data).also({ count = it }) != -1) {
out.write(data, 0, count)
}
out.closeEntry() // --> close entry after writing data

Java: ZIP Files downloaded with HTTPUrlConnection are corrupted

I am migrating a webapp from Java 8 to 11 (and Tomcat 8 to 9) and i have a Client that downloads ZIP Archive Files from a Service using the following methods:
public HTTPResponse doGet(String aUrl, HashMap<String,String> aRequestParams, HashMap<String,String> aRequestProperties)
throws Exception
{
try
{
String lUrl = aUrl;
if (aRequestParams != null && aRequestParams.size() > 0)
{
StringBuffer lBodyStringBuffer = new StringBuffer();
for(String lParam : aRequestParams.keySet())
{
String lValue = aRequestParams.get(lParam);
if(lValue != null && !"".equals(lValue.trim()))
{
if(lBodyStringBuffer.length() > 0)
{
lBodyStringBuffer.append("&");
}
lBodyStringBuffer.append(URLEncoder.encode(lParam, sDEFAULTENCODING)).append("=").append(URLEncoder.encode(lValue, sDEFAULTENCODING));
}
}
String lParamString = lBodyStringBuffer.toString();
if (lParamString != null && lParamString.length() > 0)
{
if (!(lUrl.endsWith(sURLPARAMSLEADER) || aUrl.endsWith(sURLPARAMSSEPARATOR)))
{
if (lUrl.indexOf(sURLPARAMSLEADER) > -1)
{
lUrl = lUrl + sURLPARAMSSEPARATOR;
}
else
{
lUrl = lUrl + sURLPARAMSLEADER;
}
}
lUrl = lUrl + lParamString;
}
}
HttpURLConnection lConnection = createConnection(lUrl,sREQUESTETHOD_GET,null, aRequestProperties);
HTTPResponse lReturn = getResponseFromConnection(lConnection);
return lReturn;
}
catch(Exception lException)
{
throw new Exception("Fehler beim Durchführen der Anfrage: " + lException.getMessage(), lException);
}
}
private HTTPResponse getResponseFromConnection(HttpURLConnection aConnection)
throws Exception
{
InputStream lConnectionInputStream = null;
ByteArrayOutputStream lResponseByteArrayOutputStream = null;
try
{
aConnection.setRequestProperty("Accept", "application/zip");
int lStatusCode = aConnection.getResponseCode();
String lResponseCharset = getCharsetFromResponseContentType(aConnection.getContentType());
if (lResponseCharset == null)
{
if (lResponseCharset == null ||lResponseCharset.trim().length() == 0)
{
lResponseCharset = "UTF-8";
}
}
if (HttpURLConnection.HTTP_OK == lStatusCode)
{
lConnectionInputStream = aConnection.getInputStream();
}
else
{
lConnectionInputStream = aConnection.getErrorStream();
}
String lMessage = "";
if (lConnectionInputStream != null)
{
lResponseByteArrayOutputStream = new ByteArrayOutputStream();
int lBufferSize = 4096;
byte[] lBuffer = new byte[lBufferSize];
int lLength = 0;
while ((lLength = lConnectionInputStream.read(lBuffer, 0, lBufferSize)) != -1)
{
lResponseByteArrayOutputStream.write(lBuffer, 0, lLength);
}
byte[] lResponseByte = lResponseByteArrayOutputStream.toByteArray();
lMessage = new String (lResponseByte,lResponseCharset);
}
HTTPResponse lReturn = new HTTPResponse(lStatusCode, lMessage);
return lReturn;
}
catch(Exception lException)
{
throw lException;
}
finally
{
if (lResponseByteArrayOutputStream != null)
{
try{lResponseByteArrayOutputStream.close();}catch(Exception e){}
}
if (lConnectionInputStream != null)
{
try{lConnectionInputStream.close();}catch(Exception e){}
}
}
}
This is how i actually call the service via HTTP Get and save the data:
HTTPResponse lResponse = new HTTPRequest().doGet("http://localService.com/, null, null);
FileOutputStream lFileOutputStream = new FileOutputStream("exampleFile.zip", false);
lFileOutputStream.write(lResponse.getMessage().getBytes());
lFileOutputStream.close();
So this used to Work before and i am getting a headache about what might have changed. When i download the ZIP File with url in my Browser, everything seems fine, so the Service seems to work. But with my client the ZIP Files get corrupted and cannot be opened. The are not empty but they differ in size: surprisingly the corrupted files are about 50% larger than the ones downloaded via browser.
Does anyone know what the problem here is?
Ok,the problem was the return type String of the method getResponseFromConnection. I changed it and now write the file directly with the File.copy() method which VGR suggested.

java IOException: Broken pipe using HttpHandler to serve mp3. Only with Android browser

I am trying to serve an mp3 using HttpHandler and getting a broken pipe. It works with Google Chrome on my mac and my iPad but Android cause the HttpHander to just hang after getting the IOException and I have to restart. Using very simple code and works fine with images and html.
try {
String requestURI = t.getRequestURI().toString().substring(1);
if(requestURI.equals("") || requestURI.equals("/"))
requestURI = "index.htm";
requestURI = requestURI.replaceAll("%20", " ");
if(requestURI.contains("mp3")) {
urlToResource = new File(System.getProperty("user.home") + "/test/" +
requestURI).toURI().toURL();
}
System.out.println("Modified requestURI:" + requestURI);
if(requestURI.contains("mp3")) {
sContentType = "audio/mpeg";
} else if(requestURI.contains("png")) {
sContentType = "image/png";
} else if(requestURI.contains("jpg")) {
sContentType = "image/jpg";
} else if(requestURI.contains("favicon.ico")) {
sContentType = "content/unknown";
} else if(requestURI.contains("css")) {
sContentType = "text/css";
} else {
sContentType = "text/html";
}
if(!requestURI.contains("mp3")) {
urlToResource = new File("src/com/daford/web/" + requestURI).toURI().toURL();
}
if(urlToResource != null) {
conn = urlToResource.openConnection();
int size = conn.getContentLength();
System.out.println("file " + requestURI + " size is:" + size);
inConnectionReader = conn.getInputStream();
headers = t.getResponseHeaders();
headers.add("Content-Type", sContentType);
t.sendResponseHeaders(200, size);
os = t.getResponseBody();
int iReadByte = inConnectionReader.read();
while (iReadByte != -1) {
os.write(iReadByte);
iReadByte = inConnectionReader.read();
}
} else {
headers = t.getResponseHeaders();
headers.add("Content-Type", "text/html");
String sErrorMessage = "Error getting webpage.";
t.sendResponseHeaders(404, sErrorMessage.length());
os = t.getResponseBody();
os.write(sErrorMessage.getBytes());
}
if(os != null) {
os.close();
}
} catch (Exception e) {
e.printStackTrace();
}

Split large file into chunks

I have a method which accept file and size of chunks and return list of chunked files. But the main problem that my line in file could be broken, for example in main file I have next lines:
|1|aaa|bbb|ccc|
|2|ggg|ddd|eee|
After split I could have in one file:
|1|aaa|bbb
In another file:
|ccc|2|
|ggg|ddd|eee|
Here is the code:
public static List<File> splitFile(File file, int sizeOfFileInMB) throws IOException {
int counter = 1;
List<File> files = new ArrayList<>();
int sizeOfChunk = 1024 * 1024 * sizeOfFileInMB;
byte[] buffer = new byte[sizeOfChunk];
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
String name = file.getName();
int tmp = 0;
while ((tmp = bis.read(buffer)) > 0) {
File newFile = new File(file.getParent(), name + "."
+ String.format("%03d", counter++));
try (FileOutputStream out = new FileOutputStream(newFile)) {
out.write(buffer, 0, tmp);
}
files.add(newFile);
}
}
return files;
}
Should I use RandomAccessFile class for above purposes (main file is really big - more then 5 Gb)?
If you don't mind to have chunks of different lengths (<=sizeOfChunk but closest to it) then here is the code:
public static List<File> splitFile(File file, int sizeOfFileInMB) throws IOException {
int counter = 1;
List<File> files = new ArrayList<File>();
int sizeOfChunk = 1024 * 1024 * sizeOfFileInMB;
String eof = System.lineSeparator();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String name = file.getName();
String line = br.readLine();
while (line != null) {
File newFile = new File(file.getParent(), name + "."
+ String.format("%03d", counter++));
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile))) {
int fileSize = 0;
while (line != null) {
byte[] bytes = (line + eof).getBytes(Charset.defaultCharset());
if (fileSize + bytes.length > sizeOfChunk)
break;
out.write(bytes);
fileSize += bytes.length;
line = br.readLine();
}
}
files.add(newFile);
}
}
return files;
}
The only problem here is file charset which is default system charset in this example. If you want to be able to change it let me know. I'll add third parameter to "splitFile" function for it.
Just in case anyone is interested in a Kotlin version.
It creates an iterator of ByteArray chunks:
class ByteArrayReader(val input: InputStream, val chunkSize: Int, val bufferSize: Int = 1024*8): Iterator<ByteArray> {
var eof: Boolean = false
init {
if ((chunkSize % bufferSize) != 0) {
throw RuntimeException("ChunkSize(${chunkSize}) should be a multiple of bufferSize (${bufferSize})")
}
}
override fun hasNext(): Boolean = !eof
override fun next(): ByteArray {
var buffer = ByteArray(bufferSize)
var chunkWriter = ByteArrayOutputStream(chunkSize) // no need to close - implementation is empty
var bytesRead = 0
var offset = 0
while (input.read(buffer).also { bytesRead = it } > 0) {
if (chunkWriter.use { out ->
out.write(buffer, 0, bytesRead)
out.flush()
offset += bytesRead
offset == chunkSize
}) {
return chunkWriter.toByteArray()
}
}
eof = true
return chunkWriter.toByteArray()
}
}
Split a file to multiple chunks (in memory operation), here I'm splitting any file to a size of 500kb(500000 bytes) and adding to a list :
public static List<ByteArrayOutputStream> splitFile(File f) {
List<ByteArrayOutputStream> datalist = new ArrayList<>();
try {
int sizeOfFiles = 500000;
byte[] buffer = new byte[sizeOfFiles];
try (FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)) {
int bytesAmount = 0;
while ((bytesAmount = bis.read(buffer)) > 0) {
try (OutputStream out = new ByteArrayOutputStream()) {
out.write(buffer, 0, bytesAmount);
out.flush();
datalist.add((ByteArrayOutputStream) out);
}
}
}
} catch (Exception e) {
//get the error
}
return datalist;
}
Split files in chunks depending upon your chunk size
val f = FileInputStream(file)
val data = ByteArray(f.available()) // Size of original file
var subData: ByteArray
f.read(data)
var start = 0
var end = CHUNK_SIZE
val max = data.size
if (max > 0) {
while (end < max) {
subData = data.copyOfRange(start, end)
start = end
end += CHUNK_SIZE
if (end >= max) {
end = max
}
//Function to upload your chunk
uploadFileInChunk(subData, isLast = false)
}
// For the Last Chunk
end--
subData = data.copyOfRange(start, end)
uploadFileInChunk(subData, isLast = true)
}
If you are taking the file from the user through intent you may get file URI as content, so in that case.
Uri uri = data.getData();
InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
fileInBytes = IOUtils.toByteArray(inputStream);
Add the dependency in you build gradle to use IOUtils
compile 'commons-io:commons-io:2.11.0'
Now do a little modification in the above code to send your file to server.
var subData: ByteArray
var start = 0
var end = CHUNK_SIZE
val max = fileInBytes.size
if (max > 0) {
while (end < max) {
subData = fileInBytes.copyOfRange(start, end)
start = end
end += CHUNK_SIZE
if (end >= max) {
end = max
}
uploadFileInChunk(subData, isLast = false)
}
// For the Last Chunk
end--
subData = fileInBytes.copyOfRange(start, end)
uploadFileInChunk(subData, isLast = true)
}

Categories