Merge pull request #1876 from xhh/file-downloading-fixes

File downloading improvements for Ruby and Java
This commit is contained in:
wing328 2016-01-15 11:25:25 +08:00
commit 078f68a211
7 changed files with 112 additions and 30 deletions

View File

@ -581,6 +581,17 @@ public class ApiClient {
return params; return params;
} }
/**
* Sanitize filename by removing path.
* e.g. ../../sun.gif becomes sun.gif
*
* @param filename The filename to be sanitized
* @return The sanitized filename
*/
public String sanitizeFilename(String filename) {
return filename.replaceAll(".*[/\\\\]", "");
}
/** /**
* Check if the given MIME is a JSON MIME. * Check if the given MIME is a JSON MIME.
* JSON MIME examples: * JSON MIME examples:
@ -738,8 +749,9 @@ public class ApiClient {
// Get filename from the Content-Disposition header. // Get filename from the Content-Disposition header.
Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
Matcher matcher = pattern.matcher(contentDisposition); Matcher matcher = pattern.matcher(contentDisposition);
if (matcher.find()) if (matcher.find()) {
filename = matcher.group(1); filename = sanitizeFilename(matcher.group(1));
}
} }
String prefix = null; String prefix = null;

View File

@ -170,23 +170,37 @@ module {{moduleName}}
# from the "Content-Disposition" header if provided, otherwise a random filename. # from the "Content-Disposition" header if provided, otherwise a random filename.
# #
# @see Configuration#temp_folder_path # @see Configuration#temp_folder_path
# @return [File] the file downloaded # @return [Tempfile] the file downloaded
def download_file(response) def download_file(response)
tmp_file = Tempfile.new '', @config.temp_folder_path
content_disposition = response.headers['Content-Disposition'] content_disposition = response.headers['Content-Disposition']
if content_disposition if content_disposition
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1] filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
path = File.join File.dirname(tmp_file), filename prefix = sanitize_filename(filename)
else else
path = tmp_file.path prefix = 'download-'
end end
# close and delete temp file prefix = prefix + '-' unless prefix.end_with?('-')
tmp_file.close!
File.open(path, 'w') { |file| file.write(response.body) } tempfile = nil
@config.logger.info "File written to #{path}. Please move the file to a proper folder "\ encoding = response.body.encoding
"for further processing and delete the temp afterwards" Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) do |file|
File.new(path) file.write(response.body)
tempfile = file
end
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
"explicitly with `tempfile.delete`"
tempfile
end
# Sanitize filename by removing path.
# e.g. ../../sun.gif becomes sun.gif
#
# @param [String] filename the filename to be sanitized
# @return [String] the sanitized filename
def sanitize_filename(filename)
filename.gsub /.*[\/\\]/, ''
end end
def build_request_url(path) def build_request_url(path)

View File

@ -580,6 +580,17 @@ public class ApiClient {
return params; return params;
} }
/**
* Sanitize filename by removing path.
* e.g. ../../sun.gif becomes sun.gif
*
* @param filename The filename to be sanitized
* @return The sanitized filename
*/
public String sanitizeFilename(String filename) {
return filename.replaceAll(".*[/\\\\]", "");
}
/** /**
* Check if the given MIME is a JSON MIME. * Check if the given MIME is a JSON MIME.
* JSON MIME examples: * JSON MIME examples:
@ -737,8 +748,9 @@ public class ApiClient {
// Get filename from the Content-Disposition header. // Get filename from the Content-Disposition header.
Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
Matcher matcher = pattern.matcher(contentDisposition); Matcher matcher = pattern.matcher(contentDisposition);
if (matcher.find()) if (matcher.find()) {
filename = matcher.group(1); filename = sanitizeFilename(matcher.group(1));
}
} }
String prefix = null; String prefix = null;

View File

@ -277,4 +277,17 @@ public class ApiClientTest {
assertEquals(values.size(), pairValueSplit.length); assertEquals(values.size(), pairValueSplit.length);
} }
} }
@Test
public void testSanitizeFilename() {
assertEquals("sun", apiClient.sanitizeFilename("sun"));
assertEquals("sun.gif", apiClient.sanitizeFilename("sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename("../sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename("/var/tmp/sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename("./sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename("..\\sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename("\\var\\tmp\\sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename("c:\\var\\tmp\\sun.gif"));
assertEquals("sun.gif", apiClient.sanitizeFilename(".\\sun.gif"));
}
} }

View File

@ -170,23 +170,37 @@ module Petstore
# from the "Content-Disposition" header if provided, otherwise a random filename. # from the "Content-Disposition" header if provided, otherwise a random filename.
# #
# @see Configuration#temp_folder_path # @see Configuration#temp_folder_path
# @return [File] the file downloaded # @return [Tempfile] the file downloaded
def download_file(response) def download_file(response)
tmp_file = Tempfile.new '', @config.temp_folder_path
content_disposition = response.headers['Content-Disposition'] content_disposition = response.headers['Content-Disposition']
if content_disposition if content_disposition
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1] filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
path = File.join File.dirname(tmp_file), filename prefix = sanitize_filename(filename)
else else
path = tmp_file.path prefix = 'download-'
end end
# close and delete temp file prefix = prefix + '-' unless prefix.end_with?('-')
tmp_file.close!
File.open(path, 'w') { |file| file.write(response.body) } tempfile = nil
@config.logger.info "File written to #{path}. Please move the file to a proper folder "\ encoding = response.body.encoding
"for further processing and delete the temp afterwards" Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) do |file|
File.new(path) file.write(response.body)
tempfile = file
end
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
"explicitly with `tempfile.delete`"
tempfile
end
# Sanitize filename by removing path.
# e.g. ../../sun.gif becomes sun.gif
#
# @param [String] filename the filename to be sanitized
# @return [String] the sanitized filename
def sanitize_filename(filename)
filename.gsub /.*[\/\\]/, ''
end end
def build_request_url(path) def build_request_url(path)

View File

@ -6,12 +6,13 @@ Gem::Specification.new do |s|
s.name = "petstore" s.name = "petstore"
s.version = Petstore::VERSION s.version = Petstore::VERSION
s.platform = Gem::Platform::RUBY s.platform = Gem::Platform::RUBY
s.authors = ["Zeke Sikelianos", "Tony Tam"] s.authors = [""]
s.email = ["zeke@wordnik.com", "fehguy@gmail.com"] s.email = [""]
s.homepage = "http://swagger.io" s.homepage = ""
s.summary = %q{A ruby wrapper for the swagger APIs} s.summary = ""
s.description = %q{This gem maps to a swagger API} s.description = ""
s.license = "Apache-2.0" s.license = ""
s.add_runtime_dependency 'typhoeus', '~> 0.2', '>= 0.2.1' s.add_runtime_dependency 'typhoeus', '~> 0.2', '>= 0.2.1'
s.add_runtime_dependency 'json', '~> 1.4', '>= 1.4.6' s.add_runtime_dependency 'json', '~> 1.4', '>= 1.4.6'

View File

@ -258,4 +258,20 @@ describe Petstore::ApiClient do
end end
end end
describe "#sanitize_filename" do
let(:api_client) { Petstore::ApiClient.new }
it "works" do
api_client.sanitize_filename('sun').should == 'sun'
api_client.sanitize_filename('sun.gif').should == 'sun.gif'
api_client.sanitize_filename('../sun.gif').should == 'sun.gif'
api_client.sanitize_filename('/var/tmp/sun.gif').should == 'sun.gif'
api_client.sanitize_filename('./sun.gif').should == 'sun.gif'
api_client.sanitize_filename('..\sun.gif').should == 'sun.gif'
api_client.sanitize_filename('\var\tmp\sun.gif').should == 'sun.gif'
api_client.sanitize_filename('c:\var\tmp\sun.gif').should == 'sun.gif'
api_client.sanitize_filename('.\sun.gif').should == 'sun.gif'
end
end
end end