How to download image files via streams in the temp directory, I've following code and I'm stuck and need guidance with the seek and the count part. There are some wrapper approaches but I'm looking specifically for while loop approach for RAM efficiency reasons.
Writing
let tempFileName = Path.GetTempFileName()
let request = WebRequest.CreateHttp "http://example.com/image.png"
use response = request.GetResponse() :?> HttpWebResponse
use stream = response.GetResponseStream()
let buffer = Array.zeroCreate 1024
use reader = new BinaryReader(stream)
use memoryStream = new MemoryStream()
use fileStream = new FileStream(tempFileName, FileMode.Open)
while not (reader.PeekChar() <> -1) do
fileStream.Write(reader.ReadBytes(1024), 0, 1024)
return Ok (tempFileName)
First of all, I notice that although you're creating a buffer array, you're not actually using it. Second, when I look at the BinaryReader documentation, and specifically the documentation for the ReadBytes method, I notice that it takes an int parameter and returns a byte array. This must mean that it's allocating a new array every time, which seems to be the opposite of what you intend (since you mention RAM efficiency, I assume that what you actually want is to re-use the same buffer each time).
And one other observation: the ReadBytes method says that it might return an array smaller than the requested size, if there were fewer bytes available. Your code currently isn't handling that case.
All of these can be fixed, though, by switching to the BinaryReader.Read(byte[], int, int) method instead. With this method, your while loop would look something like the following:
while not (reader.PeekChar() <> -1) do
let bytesRead = reader.Read(buffer, 0, 1024)
fileStream.Write(buffer, 0, bytesRead)
And now that we're keeping track of how many bytes were read by each Read operation, we can get rid of the PeekChar call and save ourselves some time (calling PeekChar on something you're downloading is not without cost since the library has to download the next byte, then save it somewhere so it can be returned the next time you call Read). We can do that by checking how many bytes were read at the previous call: if it was 0, then that means we're at the end of the stream. To do this, we'll move the bytesRead variable out of the loop, which means making it a mutable variable that we'll re-use every time through the loop:
let mutable bytesRead = -1
while not (bytesRead = 0) do
bytesRead <- reader.Read(buffer, 0, 1024)
fileStream.Write(buffer, 0, bytesRead)
Or if you want to be slightly more explicit about the fact that you're skipping Write if bytesRead is 0, you could add an if block:
let mutable bytesRead = -1
while not (bytesRead = 0) do
bytesRead <- reader.Read(buffer, 0, 1024)
if bytesRead > 0 then
fileStream.Write(buffer, 0, bytesRead)
That last if statement isn't strictly necessary, though: FileStream.Write should just return without doing anything if it's asked to write 0 bytes. However, since that's not documented anywhere that I could find, I added the if statement in this last code sample just to be on the safe side.
As of .NET 4.6.2, there is System.IO.Stream#CopyTo method:
namespace FSharpBasics
module ImageCrawler =
open System.Net
open System.IO
open System.Text.RegularExpressions
let private myurl = "https://cdn.pixabay.com/photo/2016/07/06/15/29/math-1500720_960_720.jpg"
let crawler (url: string) =
let fileName = Regex.Match(url, #"\/([^\/]+)$", RegexOptions.RightToLeft).Groups.[1].Value
let request = WebRequest.CreateHttp url
let response = request.GetResponse()
use s = response.GetResponseStream()
use w = File.Create fileName
s.CopyTo w
w.Flush true
[<EntryPoint>]
let main argv =
printfn "JPEG file will be saved"
crawler myurl
printf "Saved"
0
Related
I want to create a zip from a certain file, then i convert this zip (A buffer, not a file in the disk) to bytes, then to send it by a post body to an api.
Thats the code (simplifyed) just with the essencial, and from now to the next step, how can i convert it into bytes?
pub fn to_zip() {
let buf: &mut [u8] = &mut [0u8; 65536];
let w = std::io::Cursor::new(buf);
let mut zip = zip::ZipWriter::new(w);
// * Convert the buffer to bytes
zip.finish().unwrap();
}
Sorry for a maybe confuse code first time with Rust beeing loving it so far!
zip.finish().unwrap() gives you the Cursor that you used to create the ZipWriter. You can then use into_inner to go back to a &mut [u8].
I'm exploring low level programming in F#, I do know about the risk and general "don't do this", that is not what I'm asking for.
I'm currently trying the basic stuff of just getting things to work.
right now I having the problem that I got some simple type (code below) and I put some data into the memory and trying to read that data as the struct. but it keeps thronging the System.Reflection.TargetInvocationException saying I'm trying to read protected memory?
#nowarn "9"
open FSharp.NativeInterop
open System.Runtime.InteropServices
type msgtype = OK = 0 | ERR = 1
[<type: Struct; StructLayout(LayoutKind.Explicit)>]
type msg =
[<field: FieldOffsetAttribute(0); MarshalAs(UnmanagedType.I1)>]
val tp : byte
[<field: FieldOffsetAttribute(1); MarshalAs(UnmanagedType.I4)>]
val content : int
let memory =
Marshal.AllocHGlobal(1024)
let read n =
let adr = memory
let ptr = NativePtr.ofNativeInt<_> memory
NativePtr.read ptr
let write item adr =
let adr' = memory + adr
let ptr = NativePtr.ofNativeInt adr'
NativePtr.write ptr item
write 1uy memory
write 654 (memory+1n)
let m : msg = read memory
Marshal.FreeHGlobal memory
I solved the problem my self.
Apparently the write function was the problem.
I changed it to
let write add item =
NativePtr.write (NativePtr.ofNativeInt<_> add) item
and it just worked.
can say if it is the declarings that course to problem only that somehow it is different?
I am converting old Swift 2 code to Swift 3 and I am facing difficulty in converting following for loop
for (var nSize = merkleTree.count; nSize > 1; nSize = (nSize + 1) / 2)
{
//...
}
There are many similar question on SO but I didn't find any solution applicable to my problem Or I didn't understand.
I thought that below code will work but it is giving error.
for var nSize in merkleTree.count.stride(to:1, by:(nSize+1)/2)
Use of unresolved identifier 'nSize'
I don't think this can be written using for anymore, but you can use while loop to get the job done:
var nSize = merkleTree.count
while nSize > 1 {
// loop body
nSize = (nSize + 1) / 2
}
I would expect stride not to work in this case, because as your error states, you cannot use nSize as the stride parameter - nSize is iterating variable that gets declared based on the range, so you need the range to exist. At least that's my interpretation of the error (I know that theoretically you can generate range based on the previously generated item, but obviously stride does not work that way).
I believe you can find a way to generate a proper array of values using reduce (because I was able to, see below, maybe you can make it simpler), or by implementing your own stride that would accept a closure instead of a step (which would allow you to compute next item based on previous one), but both approaches are more complicated and obscure than using the simple while loop, so I personally prefer the while loop.
My not so nice reduce implementation (in result it uses an array and not a range, since by looking at NSRange I don't think you can create a range that does not step by 1):
let merkleTree = [1,2,3,4,5,6,7,8,9]
let numberOfDivisions = Int(log2(Double(merkleTree.count))) + 1
let startValue = merkleTree.count
let nSizes = (0..<numberOfDivisions).reduce([startValue]) { (result, next) -> [Int] in
var newResult = result
newResult.append((result.last! + 1) / 2)
return newResult
}
print(nSizes)
// and now you can for-in it:
for nSize in nSizes {
// ...
}
I want to use a subtitle API. It requires a md5 hash of first and last 64kb of the video file. I know how to do the md5 part just want to know how will I achieve to get the 128kb of data.
Here is the solution to the problem in Java which I am unable to implement in Swift. Stack
I have a video URL, How would I get the first and last 64kb from it? Get on AlamoFire then what?
below is how it's done in Java,
FileInputStream in = new FileInputStream("d:/1.avi");
byte[] a = new byte[64 * 1024];
in.read(a); //head
long p = in.getChannel().size() - 64 * 1024;
in.getChannel().position(p);
in.read(a); //tail
Here is how to do it correctly:
let data = try! Data(contentsOf: URL(string: <#Insert your URL#>)!) // should do some unwrapping precautions here
// first 64 bytes
let first = data.subdata(in: 0 ..< 65336) // 65336 bytes = 1kb (if 1kb = 1024 bytes)
// last 64 bytes
let last = data.subdata(in: (data.count - 65336)..<data.count) // data.count - 65366 = last 64 bytes of the file
So first you download the file (eg with Alamofire). Once that is done, place the URL into the string: parameter of the URL initialiser.
Then, use the variables first and last to get the md5.
I have the code:
let data = Data(bytes: UnsafePointer<UInt8>(audioBuffer.mData), count: Int(bufferSize))
and
let u16 = UnsafePointer<Int32>(audioBuffer.mData).pointee
Both of which work in Swift 2.3 but not in Swift 3. How do I convert them so they act equivalently? (and why?)
To read 16-bit audio samples from Audio Unit callback buffers in Swift 3, I use:
let bufferPointer = UnsafeMutableRawPointer(mBuffers.mData)
if var bptr = bufferPointer {
for i in 0..<(Int(frameCount)) {
let oneSampleI16 = bptr.assumingMemoryBound(to: Int16.self).pointee
// do something with the audio sample
bptr += 1
}
}
The rest of the Audio Session and Audio Unit code is in this gist: https://gist.github.com/hotpaw2/630a466cc830e3d129b9
I can't say I understand this well, nor have I read the document, but it looks like swift3 pointer casts are scoped to avoid or limit aliasing, so you can't (easily) have two different views of the same piece of memory, or at least not for very long. This means you must either copy the cast data out or do whatever you need to do within a cast callback.
Why eliminate aliasing? I guess it makes for happier compilers.
For Data:
// [NS]Data. probably copying the data
Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
For numeric arrays:
// cast the data to Int32s & (optionally) copy the data out
let umpInt32 = audioBuffer.mData!.assumingMemoryBound(to: Int32.self)
let frameCount = Int(audioBuffer.mDataByteSize/4)
var u32 = [Int32](repeating: 0, count: frameCount)
// copy data from buffer
u32.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: umpInt32, count: frameCount)
}
p.s. there's some confusion in your code. is u16 supposed to be an array of Int32s? Or UInt16s? Or something else?
Check the latest reference of Data.init(bytes:count:).
The type of the parameter bytes is UnsafeRawPointer, which accepts UnsafeMutableRawPointer. And the type of AudioBuffer.mData is UnsafeMutableRawPointer?. You have no need to convert using initializer.
let data = Data(bytes: audioBuffer.mData!, count: Int(bufferSize))
(You just need to explicitly unwrap mData, as it is imported as nullable type, UnsafeMutableRawPointer?, but you need to pass non-nil UnsafeRawPointer (or UnsafeMutableRawPointer).
The second example, you'd better check what sort of methods are available for UnsafeMutableRawPointer. You can find load(fromByteOffset:as:) method, and can use it like this.
let i32 = audioBuffer.mData!.load(as: Int32.self)
`load(