Ionic/Cordova File plugin fails with to write file when downloading a large number of documents in succession on iOS device - ios

The problem as the user sees it:
User has long list of documents they need to download to iOS device (200+)
User starts the download, with each file downloaded in succession.
At the end of the download queue, they discover that one of the files fails (and its always one of two specific files that are 25MB+)
They retry the job (which only downloads the failed document) and it succeeds
What I'm seeing as a developer:
My app pulls down the document as a blob
When I inspect the blob (within my Typescript app code), it has a size > 0
I call this.file.writeFile(directoryPath, fileName, blob, {replace: true}), which calls the Ionic File wrapper around Cordova File Plugin
However, when I look at the blob in write of FileWriter.js, it has a size of zero
This all results in the error spitting out as:
{"type":"error","bubbles":false,"cancelBubble":false,"cancelable":false,"lengthComputable":false,"loaded":0,"total":0,"target":{"fileName":"","length":0,"localURL":"cdvfile://localhost/persistent/downloaded-assets/9ce34f8a-6201-4023-9f5b-de6133bd5699/{{redacted}}","position":0,"readyState":2,"result":null,"error":{},"onwritestart":null,"onprogress":null,"onwriteend":null,"onabort":null}}
What I'm getting from this is that somewhere between calling file.writeFile on the Ionic File wrapper in my typescript code and the FileWriter.write method in the Cordova package, my blob is getting corrupted, lost, or emptied somehow.
It's difficult to debug as that layer in-between these two points is minified in the xCode debugger, so it would also be nice to hear some suggestions about how I might be able to debug this myself better.
Do we have any idea what might be going on here? Is it a memory issue on iOS? Does Cordova timeout over multiple repeated requests?
A few things to note:
The full list of files download fine every time on I try within the iOS xCode simulator. This is leading me to believe it might be a memory issue, but I'm not sure.
The failure always happens after about 200 files, and on one of two files that are 25-30MB+
As far as debugging goes, the earliest I can see my blob reduced to 0 is here https://github.com/apache/cordova-plugin-file/blob/4a92bbbea755aa9e5bf8cfd160fb9da1cd3287cd/www/FileWriter.js#L107 (though I might be debugging incorrectly)
EDIT - After a little more digging, I was able to see exactly where the Ionic plugin went wrong:
The code I used:
private writeFileInChunks(writer: FileWriter, file: Blob) {
console.log('SIZE OF FILE AT START', file.size);
const BLOCK_SIZE = 1024 * 1024;
let writtenSize = 0;
function writeNextChunk() {
const size = Math.min(BLOCK_SIZE, file.size - writtenSize);
console.log('CALCULATED SIZE:', size);
const chunk = file.slice(writtenSize, writtenSize + size);
console.log('SIZE OF CHUNK TO WRITE', chunk.size)
writtenSize += size;
writer.write(chunk);
}
return getPromise<any>((resolve, reject) => {
writer.onerror = reject as (event: ProgressEvent) => void;
writer.onwrite = () => {
if (writtenSize < file.size) {
writeNextChunk();
} else {
resolve();
}
};
writeNextChunk();
});
}
The output for the failed document:
SIZE OF FILE AT START: 34012899
CALCULATED SIZE: 1048576
SIZE OF CHUNK TO WRITE: 0
On retry:
SIZE OF FILE AT START: 34012899
CALCULATED SIZE: 1048576
SIZE OF CHUNK TO WRITE: 1048576
CALCULATED SIZE: 1048576
SIZE OF CHUNK TO WRITE: 1048576
...
...
...
CALCULATED SIZE: 458467
SIZE OF CHUNK TO WRITE: 458467
So for whatever reason, after a large number of previous downloads, that file.slice step results in an empty/corrupted blob.
Any ideas on how to correct this?
Ran another test with some expanded logging:
private writeFileInChunks(writer: FileWriter, file: Blob) {
...
function writeNextChunk() {
const size = Math.min(BLOCK_SIZE, file.size - writtenSize);
console.log('CALCULATED SIZE:', size);
console.log('WRITTEN SIZE', writtenSize);
console.log('SUMS TO:', writtenSize + size)
console.log('FILE SIZE BEFORE SLICE:', file.size);
const chunk = file.slice(writtenSize, writtenSize + size);
console.log('SIZE OF CHUNK TO WRITE', chunk.size);
writtenSize += size;
writer.write(chunk);
}
...
...
}
Output came to:
CALCULATED SIZE: 1048576
WRITTEN SIZE: 0
SUMS TO: 1048576
FILE SIZE BEFORE SLICE: 34012899
SIZE OF CHUNK TO WRITE 0
Further confirming the issue

Related

What are gl.MAX_TEXTURE_SIZE and gl.MAX_RENDERBUFFER_SIZE exacly?

I would like to display a graphic (map) in a canvas.
Now this error message appears for large displays:
WebGL warning: width/height: Requested size 8316x3141 was too large, but resize to 4158x1570 succeeded.
I found this code snippet on the website https://docs.developer.canva.com/apps/frontend-development/webgl.
// Ensure we can render images this size
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
const maxRenderBufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
const maxSize = Math.max(state.image.width, state.image.height);
if (maxSize > maxTextureSize || maxSize > maxRenderBufferSize) {
throw new Error("Failed to render image. The image is too large.");
}
But neither https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices nor https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getParameter explain to me exactly what the parameters gl.MAX_TEXTURE_SIZE and MAX_RENDERBUFFER_SIZE mean. I can't find any other documentation.
For me, the error already occurs when my image is much smaller.
Example
maxTextureSize = 16384
maxRenderBufferSize = 16384
mymaxSize = 8316
shows the warnung
WebGL warning: width/height: Requested size 8316x3141 was too large, but resize to 4158x1570 succeeded.
and does not render properly.
For 1D and 2D textures (and any texture types that use similar dimensionality, like cubemaps) the max size of either dimension is GL_MAX_TEXTURE_SIZE
Khronos OpenGL wiki
The same applies for MAX_RENDERBUFFER_SIZE except that it pertains to renderbuffers created via createRenderbuffer or implicitly by the canvas as a backbuffer.
There's been some GPU drivers that falsely report sizes that they're not able to support, are you by chance using some age-old integrated or mobile graphics solution?

How to process video with OpenCV2 in Google Cloud function?

Starting point:
There is video called myVideo.mp4 in a folder (/1_original_videos) in a Bucket called myBucket in Google Cloud Storage.
myBucket
-->/1_original_video
-->myVideo.mp4
Goal:
The goal is to take this video, split it into chunks in a Cloud Function myCloudFunction and save the chunks in a subfolder called chunks in myBucket. The part of dividing into chunks is not a problem. The problem is reading the video.
myCloudFunction must be triggered with an HTTP trigger.
_______________
myVideo.mp4 ---->|myCloudFunction|----> chunk0.mp4, chunk1.mp4, chunk2.mp4, ... , chunkN-1.mp4
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
^
|
|
|
HTTP trigger
If the video were on my local computer, in order to read it, the following would be enough:
import cv2
cap = cv2.VideoCapture("/some/path/in/my/local/computer/myVideo.mp4")
Attempts:
Path with authenticated URL:
import cv2
cap = cv2.VideoCapture("https://storage.cloud.google.com/myBucket/1_original_videos/myVideo.mp4")
When testing this approach, this is the resulting message (see complete code below):
"File Cannot be Opened"
Complete code:
import cv2
def video2chunks(request):
# Request:
REQUEST_JSON = request.get_json()
#If the HTTP contains a key called "start" (e.g. "{"start":"whatever"}"):
if REQUEST_JSON and 'start' in REQUEST_JSON:
try:
# Create VideoCapture object:
cap = cv2.VideoCapture("https://storage.cloud.google.com/myBucket/1_original_videos/myVideo.mp4")
# If no VideoCapture object is created:
if not cap.isOpened():
message = "File Cannot be Opened"
# If a Videocapture object is created, compute some of the video parameters:
else:
fps = int(cap.get(cv2.CAP_PROP_FPS))
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = int(cv2.VideoWriter_fourcc('X','V','I','D')) # XVID codecs
message = "Video downloaded successfully. Some params are: "
message += "FPS= " + str(fps) + " | size= " + str(size)
except Exception as e:
message = str(e)
else:
message = "You did not provide a key called start "
return message
I have been trying to find examples or a better way to do this in a Cloud Function but so far have been unsuccessful. Any alternatives would also be very much appreciated.
I'm not aware whether the cv2 library supports reading directly from Cloud Storage in some way. Nonetheless as Christoph points out you may download the file, process it and upload the results. The code will be essentially the same as running locally.
One thing to note is that Cloud Functions offer a temporal directory which is the way I chose to store the image. However it's important to know that any file stored there is actually consuming part of your function RAM, so the allocated function memory should be sized accordingly. Also you may notice the temp files are deleted before exiting the function, this is just a best practice in Cloud Functions.
import cv2
import os
from google.cloud import storage
def myfunc(request):
# Substitute the variables below for whatever suits your needs
# BUCKET_ID :: The bucket ID
# INPUT_IMAGE_GCS :: Path to GCS object
# OUTPUT_IMAGE_PATH :: Path to save the resulting image/video
# Read video and save to /tmp directory
bucket = storage.Client().bucket(BUCKET_ID)
blob = bucket.blob(INPUT_IMAGE_GCS)
blob.download_to_filename('/tmp/video.mp4')
# Video processing stuff
vidcap = cv2.VideoCapture('/tmp/video.mp4')
success, image = vidcap.read()
cv2.imwrite("/tmp/frame.jpg", image)
# Save results to GCS
img_blob = bucket.blob('potato/frame.jpg')
img_blob.upload_from_filename(OUTPUT_IMAGE_PATH)
# Delete tmp resources to free memory
os.remove('/tmp/video.mp4')
os.remove('/tmp/frame.jpg')
return '', 200

Cordova File Transfer download fails ios 9 - Response Code 200

I try to download a file with cordova.
These setting for ios:
Cordova 6.3.1, file plugin 3.0.0, file transfer plugin 1.4.0, Xcode 7, ios 9.3.4 on test system
But also in a Windows 10 uwp-App.
The download works sometimes (mainly for smaller files) but fails to download the file in some other cases. So i assume the code in general works fine (and did so for the last year (with older Cordova versions).
A file gets created everytime and the response code is 200 so everything seems fine for my system, but the created file is only a few bytes large in size.
fileSystem.root.getDirectory(folderName, {create: true}, function(dirEntry){
var path = dirEntry.toURL() + '/' + fileName;
var ft = new FileTransfer();
var uri = encodeURI(url) + '?' + new Date().getMilliseconds();
ft.download(sessionurl, downloadPath, function(entry) {
entry.file(
function(file) {
onsuccess(file);
},
function(error) {
onerror(error);
}
);
}
Any advice would be great.
After some checking I found the problem. My server delivers files via php. For larger files the allowed memory size was too small. To fix it I had to raise the allowed memory size on the server.

How Do I read size of an directory in a softlayer cloud storage, using jCloud Swift api

I am trying to set a quota based on directory, say about 5 mb. And Hence I need to read the size of present blobs in a directory.
I am able to get the size of individual BLOB using the below code,
if(containerName!=null && objectName!=null){
BlobMetadata metaData = blobStore.blobMetadata(containerName, objectName);
if(metaData !=null){
userMetaData = metaData.getUserMetadata();
}
ContentMetadata contMetadata = metaData.getContentMetadata();
System.out.println("Object Size "+ contMetadata.getContentLength());
}
Just not able to find out if I can check the size of all the blobs in a directory with out looping through all the blob metadata.
The container will have that info:
http://jclouds.apache.org/reference/javadoc/1.9.x/org/jclouds/openstack/swift/v1/domain/Container.html#getBytesUsed()

How can I catch corrupt JPEGs when loading an image with imread() in OpenCV?

OpenCV says something like
Corrupt JPEG data: premature end of data segment
or
Corrupt JPEG data: bad Huffman code
or
Corrupt JPEG data: 22 extraneous bytes before marker 0xd9
when loading a corrupt jpeg image with imread().
Can I somehow catch that? Why would I get this information otherwise?
Do I have to check the binary file on my own?
OpenCV (version 2.4) does not overwrite the basic error handling for libjpeg, making them 'uncatchable'. Add the following method to modules/highgui/src/grfmt_jpeg.cpp, right below the definition of error_exit():
METHODDEF(void)
output_message( j_common_ptr cinfo )
{
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
(*cinfo->err->format_message) (cinfo, buffer);
/* Default OpenCV error handling instead of print */
CV_Error(CV_StsError, buffer);
}
Now apply the method to the decoder error handler:
state->cinfo.err = jpeg_std_error(&state->jerr.pub);
state->jerr.pub.error_exit = error_exit;
state->jerr.pub.output_message = output_message; /* Add this line */
Apply the method to the encoder error handler as well:
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = error_exit;
jerr.pub.output_message = output_message; /* Add this line */
Recompile and install OpenCV as usual. From now on you should be able to catch libjpeg errors like any other OpenCV error. Example:
>>> cv2.imread("/var/opencv/bad_image.jpg")
OpenCV Error: Unspecified error (Corrupt JPEG data: 1137 extraneous bytes before marker 0xc4) in output_message, file /var/opencv/opencv-2.4.9/modules/highgui/src/grfmt_jpeg.cpp, line 180
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
cv2.error: /var/opencv/opencv-2.4.9/modules/highgui/src/grfmt_jpeg.cpp:180: error: (-2) Corrupt JPEG data: 1137 extraneous bytes before marker 0xc4 in function output_message
(I've submitted a pull request for the above but it got rejected because it would cause issues with people reading images without exception catching.)
Hope this helps anyone still struggling with this issue. Good luck.
It could be easier to fix the error in the file instead of trying to repair the loading function of OpenCV. If you are using Linux you can use ImageMagick to make reparation to a set of images (is usual to have it installed by default):
$ mogrify -set comment 'Image rewritten with ImageMagick' *.jpg
This command changes a property of the file leaving the image data untouched. However, the image is loaded and resaved, eliminating the extra information that causes the corruption error.
If you need more information about ImageMagick you can visit their website: http://www.imagemagick.org/script/index.php
You cannot catch it if you use imread(). However there is imdecode() function that is called by imread(). Maybe it gives you more feedback. For this you would have to load the image into memory on your own and then call the decoder.
It boils down to: You have to dig through the OpenCV sources to solve your problem.
i had to deal with this recently and found a solution over here
http://artax.karlin.mff.cuni.cz/~isa_j1am/other/opencv/
i just need to make 2 edits # $cv\modules\highgui\src\grfmt_jpeg.cpp.
--- opencv-1.0.0.orig/otherlibs/highgui/grfmt_jpeg.cpp 2006-10-16 13:02:49.000000000 +0200
+++ opencv-1.0.0/otherlibs/highgui/grfmt_jpeg.cpp 2007-08-11 09:10:28.000000000 +0200
## -181,7 +181,7 ##
m_height = cinfo->image_height;
m_iscolor = cinfo->num_components > 1;
- result = true;
+ result = (cinfo->err->num_warnings == 0);
}
}
## -405,8 +405,9 ##
icvCvt_CMYK2Gray_8u_C4C1R( buffer[0], 0, data, 0, cvSize(m_width,1) );
}
}
- result = true;
+
jpeg_finish_decompress( cinfo );
+ result = (cinfo->err->num_warnings == 0);
}
}
I am using opencv python package to read some image and also met this error message. This error can not be catch by Python. But if you want to find which image is corrupted without recompiling opencv as #Robbert suggested, you can try the following method.
First, you can pinpoint the directory where the corrupt images reside, which is fairly easy. Then you go to the directory, and use mogrify command line tool provided by ImageMagick to change the image meta info, as suggest by #goe.
mogrify -set comment "errors fixed in meta info" -format png *.jpg
The above command will convert the original jpg image to png format and also clean the original image to remove errors in meta info. When you run mogrify command, it will also output some message about which image is corrupted in the directory so that you can accurately find the corrupted image.
After that, you can do whatever you want with the original corrupted jpg image.
Any one stumbles upon this post and reads this answer.
I had to get hold of a corrupted image file.
These websites can help you corrupt your file
Corrupt a file - The file corrupter you were looking for!
CORRUPT A FILE ONLINE
Corrupt my File
First and the third website was not that much useful.
Second website is interesting as I could set the amount of file that I need to corrupt.
OpenCV version I used here is 3.4.0
I used normal cv2.imread(fileLocation)
fileLocation Location of corrupted image file
OpenCV didn't show any error message for any of the corrupted files used here
First and Third website only gave one file and both had None stored in them, when I tried to print them
Second website did let me decide the amount of file that was needed to be corrupted
Corruption% Opencv message on printing the image
4% None
10% None
25% None
50% None Corrupt JPEG data: 3 extraneous bytes before marker 0x4f
75% None Corrupt JPEG data: 153 extraneous bytes before marker 0xb2
100% Corrupt JPEG data: 330 extraneous bytes before marker 0xc6 None
I guess the only check we have to make here would be
if image is not None:
Do your code or else pop an error
You can redirect stderr to a file, then after imread, search for the string "Huffman" inside that file. After searching the file, empty it. It works for me and now I am able to discard corrupted images and just process good ones.
If you load your image with imdecode, you can check errno :
std::vector<char> datas();
//Load yout image in datas here
errno = 0;
cv::Mat mat = cv::imdecode(datas, -1);
if (errno != 0)
{
//Error
}
(tested on OpenCV 3.4.1)
I found that the issue is in libjpeg. If OpenCV uses it, it gets error
Corrupt JPEG data: 22 extraneous bytes before marker 0xd9
You can try my solution to solve it. It disables JPEG during compilation. After that OpenCV cannot read/write, but it works.
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_SHARED_LIBS=OFF -D BUILD_EXAMPLES=OFF -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D WITH_JPEG=OFF -D WITH_IPP=OFF ..
I found an easy solution without the need to recompile openCV.
You can use imagemagick to detect the same errors, however it returns an error as expected. See the description here: https://stackoverflow.com/a/66283167/2887398

Resources